diff options
383 files changed, 7230 insertions, 3095 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java index f48e07812637..3b5f11bea6c8 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 @@ public abstract class JobService extends Service { * @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/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 6eeff82f1158..577260e5106f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1577,8 +1577,6 @@ public class JobSchedulerService extends com.android.server.SystemService mJobPackageTracker.notePending(jobStatus); mPendingJobQueue.add(jobStatus); maybeRunPendingJobsLocked(); - } else { - evaluateControllerStatesLocked(jobStatus); } } return JobScheduler.RESULT_SUCCESS; @@ -3050,8 +3048,6 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, " queued " + job.toShortString()); } newReadyJobs.add(job); - } else { - evaluateControllerStatesLocked(job); } } @@ -3171,7 +3167,6 @@ public class JobSchedulerService extends com.android.server.SystemService } else if (mPendingJobQueue.remove(job)) { noteJobNonPending(job); } - evaluateControllerStatesLocked(job); } } @@ -3297,7 +3292,7 @@ public class JobSchedulerService extends com.android.server.SystemService @GuardedBy("mLock") boolean isReadyToBeExecutedLocked(JobStatus job, boolean rejectActive) { - final boolean jobReady = job.isReady(); + final boolean jobReady = job.isReady() || evaluateControllerStatesLocked(job); if (DEBUG) { Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() @@ -3372,12 +3367,17 @@ public class JobSchedulerService extends com.android.server.SystemService return !appIsBad; } + /** + * Gets each controller to evaluate the job's state + * and then returns the value of {@link JobStatus#isReady()}. + */ @VisibleForTesting - void evaluateControllerStatesLocked(final JobStatus job) { + boolean evaluateControllerStatesLocked(final JobStatus job) { for (int c = mControllers.size() - 1; c >= 0; --c) { final StateController sc = mControllers.get(c); sc.evaluateStateLocked(job); } + return job.isReady(); } /** diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 2d9a99cf0d8c..9bbc4a67a79a 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -538,6 +538,7 @@ package android.app.admin { 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 95e446dde4da..021f932c562c 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -45,6 +45,7 @@ import android.os.TransactionTooLargeException; 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 class ActivityManagerInternal { */ 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/Notification.java b/core/java/android/app/Notification.java index 63da0a231286..d37576092af2 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2916,6 +2916,14 @@ public class Notification implements Parcelable } } + 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 4d3338beded5..a8a2ad1bb8df 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -16825,6 +16825,23 @@ public class DevicePolicyManager { } /** + * 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 9b0b18ac74ec..9795caba95b8 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -608,4 +608,6 @@ interface IDevicePolicyManager { 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 154068eb9e09..307f30619be2 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 @@ public class Intent implements Parcelable, Cloneable { 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 @@ public class Intent implements Parcelable, Cloneable { } 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 class Intent implements Parcelable, Cloneable { 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 048289f56a0c..960d10adbfbb 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2628,6 +2628,15 @@ public class PackageParser { 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 @@ public class PackageParser { 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 3e1c5bb3d7ec..8cc4cdb955ca 100644 --- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java @@ -316,6 +316,15 @@ public class FrameworkParsingPackageUtils { 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 @@ public class FrameworkParsingPackageUtils { 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/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index ce6e1c7c676f..08ba5b6c268c 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -557,15 +557,25 @@ public class CompatibilityInfo implements Parcelable { boolean applyToSize) { inoutDm.density = inoutDm.noncompatDensity * invertedRatio; inoutDm.densityDpi = (int) ((inoutDm.noncompatDensityDpi * invertedRatio) + .5f); + // Note: since this is changing the scaledDensity, you might think we also need to change + // inoutDm.fontScaleConverter to accurately calculate non-linear font scaling. But we're not + // going to do that, for a couple of reasons (see b/265695259 for details): + // 1. The first case is only for apps targeting SDK < 4. These ancient apps will just have + // to live with linear font scaling. We don't want to make anything more unpredictable. + // 2. The second case where this is called is for scaling down games. But it is called in + // two situations: + // a. When from ResourcesImpl.updateConfiguration(), we will set the fontScaleConverter + // *after* this method is called. That's the only place where the app will actually + // use the DisplayMetrics for scaling fonts in its resources. + // b. Sometime later by WindowManager in onResume or other windowing events. In this case + // the DisplayMetrics object is never used by the app/resources, so it's ok if + // fontScaleConverter is null because it's not being used to scale fonts anyway. inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * invertedRatio; inoutDm.xdpi = inoutDm.noncompatXdpi * invertedRatio; inoutDm.ydpi = inoutDm.noncompatYdpi * invertedRatio; if (applyToSize) { inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f); inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f); - - float fontScale = inoutDm.scaledDensity / inoutDm.density; - inoutDm.fontScaleConverter = FontScaleConverterFactory.forScale(fontScale); } } diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java index 6b09c303e3cd..5eb65262918d 100644 --- a/core/java/android/content/res/FontScaleConverterFactory.java +++ b/core/java/android/content/res/FontScaleConverterFactory.java @@ -34,6 +34,8 @@ public class FontScaleConverterFactory { @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 @@ public class FontScaleConverterFactory { 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 @@ public class FontScaleConverterFactory { */ @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 72a3f6c0bc88..b4533042b4a3 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -53,12 +53,16 @@ import android.view.Surface; 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 @@ public final class DisplayManager { @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 @@ public final class DisplayManager { * @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 @@ public final class DisplayManager { 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); + private Display[] getDisplays(int[] displayIds, Predicate<Display> predicate) { + ArrayList<Display> tmpDisplays = new ArrayList<>(); + for (int displayId : displayIds) { + Display display = getOrCreateDisplay(displayId, /*assumeValid=*/true); + if (predicate.test(display)) { + tmpDisplays.add(display); } } + return tmpDisplays.toArray(new Display[tmpDisplays.size()]); } - @GuardedBy("mLock") - private void addDisplaysLocked( - ArrayList<Display> displays, int[] displayIds, int matchType, int flagMask) { - for (int displayId : displayIds) { - if (displayId == DEFAULT_DISPLAY) { - continue; - } - - 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; - - display = mGlobal.getCompatibleDisplay(displayId, resources); - if (display != null) { - mDisplayCache.put(display); + 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; + } + + 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/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 2fec02f91e39..a0cceae98ba9 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -39,7 +39,6 @@ import android.os.InputEventInjectionSync; 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 final class InputManager { */ 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 5462171be7e1..c0877d3ad8e2 100644 --- a/core/java/android/hardware/input/InputManagerGlobal.java +++ b/core/java/android/hardware/input/InputManagerGlobal.java @@ -1252,7 +1252,7 @@ public final class InputManagerGlobal { /** * @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 cdf9ea5131ba..6cd32ff1e8e5 100644 --- a/core/java/android/hardware/input/InputSettings.java +++ b/core/java/android/hardware/input/InputSettings.java @@ -25,6 +25,7 @@ import android.annotation.TestApi; 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 @@ public class InputSettings { 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/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java index fb2f4ad2abe6..1a1df6f5f989 100644 --- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java +++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java @@ -165,8 +165,7 @@ public final class CredentialProviderInfoFactory { Slog.w(TAG, "Context is null in isSystemProviderWithValidPermission"); return false; } - return PermissionUtils.isSystemApp(context, serviceInfo.packageName) - && PermissionUtils.hasPermission(context, serviceInfo.packageName, + return PermissionUtils.hasPermission(context, serviceInfo.packageName, Manifest.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE); } diff --git a/core/java/android/service/credentials/PermissionUtils.java b/core/java/android/service/credentials/PermissionUtils.java index d958111f2e0e..2baf7096708b 100644 --- a/core/java/android/service/credentials/PermissionUtils.java +++ b/core/java/android/service/credentials/PermissionUtils.java @@ -17,7 +17,6 @@ package android.service.credentials; import android.content.Context; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; /** @@ -33,21 +32,5 @@ public class PermissionUtils { return context.getPackageManager().checkPermission(permission, packageName) == PackageManager.PERMISSION_GRANTED; } - - /** Checks whether the given package name is a system app on the device **/ - public static boolean isSystemApp(Context context, String packageName) { - try { - ApplicationInfo appInfo = - context.getPackageManager() - .getApplicationInfo(packageName, - PackageManager.ApplicationInfoFlags.of( - PackageManager.MATCH_SYSTEM_ONLY)); - if (appInfo != null) { - return true; - } - } catch (PackageManager.NameNotFoundException e) { - } - return false; - } } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 77bbeb59927a..9d3d70d4a8f7 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -112,6 +112,7 @@ import java.util.Objects; 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 @@ public abstract class WallpaperService extends Service { Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED, reportDraw ? 1 : 0, mergedConfiguration); + mIWallpaperEngine.mPendingResizeCount.incrementAndGet(); mCaller.sendMessage(msg); } @@ -1051,6 +1053,10 @@ public abstract class WallpaperService extends Service { 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 @@ public abstract class WallpaperService extends Service { } } - 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 @@ public abstract class WallpaperService extends Service { | 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 @@ public abstract class WallpaperService extends Service { 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 @@ public abstract class WallpaperService extends Service { 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 @@ public abstract class WallpaperService extends Service { 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 @@ public abstract class WallpaperService extends Service { 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 @@ public abstract class WallpaperService extends Service { 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/IntArray.java b/core/java/android/util/IntArray.java index bc0e35da37c3..511cb2df712d 100644 --- a/core/java/android/util/IntArray.java +++ b/core/java/android/util/IntArray.java @@ -43,7 +43,7 @@ public class IntArray implements Cloneable { * Creates an empty IntArray with the default initial capacity. */ public IntArray() { - this(10); + this(0); } /** diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java index 53dddeb5ad0b..9f269ed74048 100644 --- a/core/java/android/util/LongArray.java +++ b/core/java/android/util/LongArray.java @@ -48,7 +48,7 @@ public class LongArray implements Cloneable { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public LongArray() { - this(10); + this(0); } /** diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index b93e3386b7cc..330a9fce1b83 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -385,10 +385,22 @@ public class TypedValue { * * @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 8801fe0b47c8..4996f5a41c82 100644 --- a/core/java/android/view/InputMonitor.java +++ b/core/java/android/view/InputMonitor.java @@ -43,7 +43,8 @@ public final class InputMonitor implements Parcelable { 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 final class InputMonitor implements Parcelable { */ public void dispose() { mInputChannel.dispose(); + mSurface.release(); try { mHost.dispose(); } catch (RemoteException e) { @@ -95,13 +97,17 @@ public final class InputMonitor implements Parcelable { @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 @@ public final class InputMonitor implements Parcelable { return mHost; } + @DataClass.Generated.Member + public @NonNull SurfaceControl getSurface() { + return mSurface; + } + @Override @DataClass.Generated.Member public String toString() { @@ -124,7 +135,8 @@ public final class InputMonitor implements Parcelable { return "InputMonitor { " + "inputChannel = " + mInputChannel + ", " + - "host = " + mHost + + "host = " + mHost + ", " + + "surface = " + mSurface + " }"; } @@ -136,6 +148,7 @@ public final class InputMonitor implements Parcelable { dest.writeTypedObject(mInputChannel, flags); dest.writeStrongInterface(mHost); + dest.writeTypedObject(mSurface, flags); } @Override @@ -151,6 +164,7 @@ public final class InputMonitor implements Parcelable { 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 @@ public final class InputMonitor implements Parcelable { 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 @@ public final class InputMonitor implements Parcelable { }; @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 bd6224b07b16..d9872174f9be 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -410,6 +410,13 @@ public class SurfaceControlViewHost { } /** + * @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 86e7fb09c5ea..2b29e7878a5d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -133,7 +133,9 @@ import android.graphics.drawable.Drawable; 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 @@ public final class ViewRootImpl implements ViewParent, @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 @@ public final class ViewRootImpl implements ViewParent, // 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, mAccessibilityInteractionConnectionManager); mAccessibilityManager.removeHighTextContrastStateChangeListener( mHighContrastTextManager); - mDisplayManager.unregisterDisplayListener(mDisplayListener); + DisplayManagerGlobal + .getInstance() + .unregisterDisplayListener(mDisplayListener); } private void setTag() { @@ -5382,7 +5394,9 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, } 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 @@ public final class ViewRootImpl implements ViewParent, } PointerIcon pointerIcon = null; - - if (event.isStylusPointer() && mInputManager.isStylusPointerIconEnabled()) { + if (event.isStylusPointer() && mIsStylusPointerIconEnabled) { pointerIcon = mHandwritingInitiator.onResolvePointerIcon(mContext, event); } @@ -7034,14 +7047,18 @@ public final class ViewRootImpl implements ViewParent, 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/view/WindowManager.java b/core/java/android/view/WindowManager.java index 02b34786e9f2..ddcb431ee06d 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -893,7 +893,7 @@ public interface WindowManager extends ViewManager { * <application> * <property * android:name= - * "android.window.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED" + * "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED" * android:value="false"/> * </application> * </pre> @@ -901,8 +901,8 @@ public interface WindowManager extends ViewManager { * @hide */ // TODO(b/274924641): Make this public API. - String PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = - "android.window.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"; + String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = + "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"; /** * Application level {@link android.content.pm.PackageManager.Property PackageManager diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index a5e708642c9f..b65c1a17e26b 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -845,11 +845,7 @@ public class ScrollView extends FrameLayout { // 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 @@ public class ScrollView extends FrameLayout { 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 67c9f8ca0048..3fbb50581b25 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -62,6 +62,7 @@ import android.content.pm.PackageManager; 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setLastBaselineToBottomHeight(lastBaselineToBottomHeight); } if (lineHeight >= 0) { - setLineHeight(lineHeight); + if (lineHeightUnit == -1) { + setLineHeightPx(lineHeight); + } else { + setLineHeight(lineHeightUnit, lineHeight); + } } } @@ -4629,6 +4649,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @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 e0ee68337061..e44f43609256 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -94,26 +94,29 @@ public final class BackNavigationInfo implements Parcelable { @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 @@ public final class BackNavigationInfo implements Parcelable { 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 @@ public final class BackNavigationInfo implements Parcelable { dest.writeTypedObject(mOnBackNavigationDone, flags); dest.writeStrongInterface(mOnBackInvokedCallback); dest.writeBoolean(mPrepareRemoteAnimation); + dest.writeBoolean(mAnimationCallback); dest.writeTypedObject(mCustomAnimationInfo, flags); } @@ -159,7 +164,7 @@ public final class BackNavigationInfo implements Parcelable { } /** - * 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 @@ public final class BackNavigationInfo implements Parcelable { } /** + * 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 @@ public final class BackNavigationInfo implements Parcelable { + "mType=" + typeToString(mType) + " (" + mType + ")" + ", mOnBackNavigationDone=" + mOnBackNavigationDone + ", mOnBackInvokedCallback=" + mOnBackInvokedCallback + + ", mPrepareRemoteAnimation=" + mPrepareRemoteAnimation + + ", mAnimationCallback=" + mAnimationCallback + ", mCustomizeAnimationInfo=" + mCustomAnimationInfo + '}'; } @@ -343,6 +358,7 @@ public final class BackNavigationInfo implements Parcelable { private IOnBackInvokedCallback mOnBackInvokedCallback = null; private boolean mPrepareRemoteAnimation; private CustomAnimationInfo mCustomAnimationInfo; + private boolean mAnimationCallback = false; /** * @see BackNavigationInfo#getType() @@ -387,6 +403,7 @@ public final class BackNavigationInfo implements Parcelable { mCustomAnimationInfo.mWindowAnimations = windowAnimations; return this; } + /** * Set resources ids for customize activity animation. */ @@ -402,12 +419,21 @@ public final class BackNavigationInfo implements Parcelable { } /** + * @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 6480da336590..bb5fe96fdec1 100644 --- a/core/java/android/window/OnBackInvokedCallbackInfo.java +++ b/core/java/android/window/OnBackInvokedCallbackInfo.java @@ -28,15 +28,20 @@ public final class OnBackInvokedCallbackInfo implements Parcelable { @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 final class OnBackInvokedCallbackInfo implements Parcelable { 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 @@ public final class OnBackInvokedCallbackInfo implements Parcelable { 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 8066f5085a01..51382a4b265f 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -193,7 +193,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { ? ((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 2b08a5525d82..853fe2f114f7 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 @@ public class SystemUiSystemPropertiesFlags { /** 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 1b4afd6dd39f..bb8bdf57ac62 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -255,6 +255,12 @@ public final class InputMethodDebug { 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 ec9184b72ed1..6e9cd44b7818 100644 --- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java +++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java @@ -65,7 +65,10 @@ import java.lang.annotation.Retention; 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 @@ public @interface SoftInputShowHideReason { */ 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/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index 1c0da1846536..0a0785e16f2a 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -66,7 +66,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener private static final long INVALID_ID = -1; public static final int NANOS_IN_MILLISECOND = 1_000_000; - private static final int MAX_LENGTH_EVENT_DESC = 20; + private static final int MAX_LENGTH_EVENT_DESC = 127; private static final int MAX_FLUSH_ATTEMPTS = 3; private static final int FLUSH_DELAY_MILLISECOND = 60; @@ -295,7 +295,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener + ", defer=" + mDeferMonitoring + ", current=" + currentVsync); } if (mDeferMonitoring && currentVsync < mBeginVsyncId) { - markEvent("FT#deferMonitoring"); + markEvent("FT#deferMonitoring", 0); // Normal case, we begin the instrument from the very beginning, // will exclude the first frame. postTraceStartMarker(this::beginInternal); @@ -326,9 +326,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener return; } mTracingStarted = true; - markEvent("FT#begin"); - Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId); - markEvent("FT#layerId#" + mSurfaceControl.getLayerId()); + Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, mSession.getName(), mSession.getName(), + (int) mBeginVsyncId); + markEvent("FT#beginVsync", mBeginVsyncId); + markEvent("FT#layerId", mSurfaceControl.getLayerId()); mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl); if (!mSurfaceOnly) { mRendererWrapper.addObserver(mObserver); @@ -354,8 +355,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener Log.d(TAG, "end: " + mSession.getName() + ", end=" + mEndVsyncId + ", reason=" + reason); } - markEvent("FT#end#" + reason); - Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); + markEvent("FT#end", reason); + markEvent("FT#endVsync", mEndVsyncId); + Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, mSession.getName(), + (int) mBeginVsyncId); mSession.setReason(reason); // We don't remove observer here, @@ -405,10 +408,11 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC; if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false; mCancelled = true; - markEvent("FT#cancel#" + reason); + markEvent("FT#cancel", reason); // We don't need to end the trace section if it has never begun. if (mTracingStarted) { - Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); + Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, mSession.getName(), + (int) mBeginVsyncId); } // Always remove the observers in cancel call to avoid leakage. @@ -429,18 +433,19 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener /** * Mark the FrameTracker events in the trace. * - * @param desc The description of the trace event, - * shouldn't exceed {@link #MAX_LENGTH_EVENT_DESC}. + * @param eventName The description of the trace event, + * @param eventValue The value of the related trace event + * Both shouldn't exceed {@link #MAX_LENGTH_EVENT_DESC}. */ - private void markEvent(@NonNull String desc) { - if (desc.length() > MAX_LENGTH_EVENT_DESC) { - throw new IllegalArgumentException(TextUtils.formatSimple( - "The length of the trace event description <%s> exceeds %d", - desc, MAX_LENGTH_EVENT_DESC)); - } + private void markEvent(@NonNull String eventName, long eventValue) { if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) { - Trace.instant(Trace.TRACE_TAG_APP, - TextUtils.formatSimple("%s#%s", mSession.getName(), desc)); + String event = TextUtils.formatSimple("%s#%s", eventName, eventValue); + if (event.length() > MAX_LENGTH_EVENT_DESC) { + throw new IllegalArgumentException(TextUtils.formatSimple( + "The length of the trace event description <%s> exceeds %d", + event, MAX_LENGTH_EVENT_DESC)); + } + Trace.instantForTrack(Trace.TRACE_TAG_APP, mSession.getName(), event); } } @@ -572,7 +577,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener getHandler().removeCallbacks(mWaitForFinishTimedOut); mWaitForFinishTimedOut = null; - markEvent("FT#finish#" + mJankInfos.size()); + markEvent("FT#finish", mJankInfos.size()); // The tracing has been ended, remove the observer, see if need to trigger perfetto. removeObservers(); @@ -622,6 +627,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener // TODO (b/174755489): Early latch currently gets fired way too often, so we have // to ignore it for now. if (!mSurfaceOnly && !info.hwuiCallbackFired) { + markEvent("FT#MissedHWUICallback", info.frameVsyncId); Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId + ", CUJ=" + mSession.getName()); } @@ -629,6 +635,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener if (!mSurfaceOnly && info.hwuiCallbackFired) { maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos); if (!info.surfaceControlCallbackFired) { + markEvent("FT#MissedSFCallback", info.frameVsyncId); Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId + ", CUJ=" + mSession.getName()); } diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java index 134a91710c0b..efaedd1073d6 100644 --- a/core/java/com/android/internal/policy/DecorContext.java +++ b/core/java/com/android/internal/policy/DecorContext.java @@ -82,13 +82,6 @@ public class DecorContext extends ContextThemeWrapper { } 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 000000000000..17739c9c8832 --- /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 6bec6bc236bd..42d68960cafd 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -334,7 +334,7 @@ cc_library_shared { "libtimeinstate", "server_configurable_flags", "libimage_io", - "libjpegrecoverymap", + "libultrahdr", ], export_shared_lib_headers: [ // our headers include libnativewindow's public headers @@ -393,7 +393,7 @@ cc_library_shared { "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 bb3089bb397a..325ebbe885b4 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -458,6 +458,7 @@ message WindowStateProto { 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 05b38a562e29..f6c9fabb7896 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" @@ -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/attrs.xml b/core/res/res/values/attrs.xml index cd25726ac730..7de36a7113ae 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1335,7 +1335,15 @@ <!-- The container color of surface, which replaces the previous surface at elevation level 2. @hide --> <attr name="materialColorSurfaceContainer" format="color"/> - + <!-- The primary branding color for the app. By default, this is the color applied to the + action bar background. @hide --> + <attr name="materialColorPrimary" format="color"/> + <!-- The secondary branding color for the app, usually a bright complement to the primary + branding color. @hide --> + <attr name="materialColorSecondary" format="color"/> + <!-- A color that passes accessibility guidelines for text/iconography when drawn on top + of tertiary. @hide --> + <attr name="materialColorTertiary" format="color"/> </declare-styleable> <!-- **************************************************************** --> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index fab760948787..a57a0517c58d 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 ccd0dccd1364..c6462f15ec50 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/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b93a78695207..b7df6a482983 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -5035,11 +5035,9 @@ <java-symbol name="materialColorOnTertiary" type="attr"/> <java-symbol name="materialColorSurfaceDim" type="attr"/> <java-symbol name="materialColorSurfaceBright" type="attr"/> - <java-symbol name="materialColorSecondary" type="attr"/> <java-symbol name="materialColorOnError" type="attr"/> <java-symbol name="materialColorSurface" type="attr"/> <java-symbol name="materialColorSurfaceContainerHigh" type="attr"/> - <java-symbol name="materialColorTertiary" type="attr"/> <java-symbol name="materialColorSurfaceContainerHighest" type="attr"/> <java-symbol name="materialColorOnSurfaceVariant" type="attr"/> <java-symbol name="materialColorOutline" type="attr"/> @@ -5047,8 +5045,71 @@ <java-symbol name="materialColorOnPrimary" type="attr"/> <java-symbol name="materialColorOnSurface" type="attr"/> <java-symbol name="materialColorSurfaceContainer" type="attr"/> - <java-symbol name="materialColorSurfaceContainer" type="attr"/> + <java-symbol name="materialColorPrimary" type="attr"/> + <java-symbol name="materialColorSecondary" type="attr"/> + <java-symbol name="materialColorTertiary" type="attr"/> <java-symbol type="attr" name="actionModeUndoDrawable" /> <java-symbol type="attr" name="actionModeRedoDrawable" /> + + <!-- Remaining symbols for Themes --> + <java-symbol type="style" name="Theme.DeviceDefault.Autofill.Save" /> + <java-symbol type="style" name="Theme.DeviceDefault.AutofillHalfScreenDialogButton" /> + <java-symbol type="style" name="Theme.DeviceDefault.AutofillHalfScreenDialogList" /> + <java-symbol type="style" name="Theme.DeviceDefault.DayNight" /> + <java-symbol type="style" name="Theme.DeviceDefault.Dialog.Alert.DayNight" /> + <java-symbol type="style" name="Theme.DeviceDefault.Dialog.FixedSize" /> + <java-symbol type="style" name="Theme.DeviceDefault.Dialog.MinWidth" /> + <java-symbol type="style" name="Theme.DeviceDefault.Dialog.NoActionBar.FixedSize" /> + <java-symbol type="style" name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" /> + <java-symbol type="style" name="Theme.DeviceDefault.Dialog.NoActionBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Dialog.Presentation" /> + <java-symbol type="style" name="Theme.DeviceDefault.Dialog" /> + <java-symbol type="style" name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.DialogWhenLarge" /> + <java-symbol type="style" name="Theme.DeviceDefault.DocumentsUI" /> + <java-symbol type="style" name="Theme.DeviceDefault.InputMethod" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.DarkActionBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Dialog.FixedSize" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Dialog.MinWidth" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Dialog.NoActionBar.FixedSize" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Dialog.NoActionBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Dialog.Presentation" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Dialog" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.DialogWhenLarge" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.NoActionBar.Overscan" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.NoActionBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Panel" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.SearchBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Voice" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light" /> + <java-symbol type="style" name="Theme.DeviceDefault.NoActionBar.Fullscreen" /> + <java-symbol type="style" name="Theme.DeviceDefault.NoActionBar.Overscan" /> + <java-symbol type="style" name="Theme.DeviceDefault.NoActionBar.TranslucentDecor" /> + <java-symbol type="style" name="Theme.DeviceDefault.NoActionBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Notification" /> + <java-symbol type="style" name="Theme.DeviceDefault.Panel" /> + <java-symbol type="style" name="Theme.DeviceDefault.ResolverCommon" /> + <java-symbol type="style" name="Theme.DeviceDefault.SearchBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Settings.Dark.NoActionBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Settings.Dialog.Alert" /> + <java-symbol type="style" name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Settings.Dialog" /> + <java-symbol type="style" name="Theme.DeviceDefault.Settings.DialogBase" /> + <java-symbol type="style" name="Theme.DeviceDefault.Settings.DialogWhenLarge" /> + <java-symbol type="style" name="Theme.DeviceDefault.Settings" /> + <java-symbol type="style" name="Theme.DeviceDefault.System.Dialog.Alert" /> + <java-symbol type="style" name="Theme.DeviceDefault.System.Dialog" /> + <java-symbol type="style" name="Theme.DeviceDefault.SystemUI.Dialog" /> + <java-symbol type="style" name="Theme.DeviceDefault.Wallpaper.NoTitleBar" /> + <java-symbol type="style" name="Theme.DeviceDefault.Wallpaper" /> + <java-symbol type="style" name="Theme.DeviceDefault" /> + <java-symbol type="style" name="Theme.DeviceDefaultBase" /> + <java-symbol type="style" name="ThemeOverlay.DeviceDefault.Accent" /> + <java-symbol type="style" name="ThemeOverlay.DeviceDefault.Accent.Light" /> + <java-symbol type="style" name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 706845360bf4..a2d54b28dace 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -270,11 +270,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -282,6 +280,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" /> @@ -364,11 +365,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -376,6 +375,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme @@ -457,11 +459,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -469,6 +469,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and @@ -552,11 +555,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -564,6 +565,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent @@ -646,11 +650,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -658,6 +660,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be @@ -748,11 +753,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -760,6 +763,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a @@ -841,11 +847,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -853,6 +857,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar --> @@ -933,11 +940,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -945,6 +950,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width @@ -1026,11 +1034,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -1038,6 +1044,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. --> @@ -1135,11 +1144,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -1147,6 +1154,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- DeviceDefault theme for a window without an action bar that will be displayed either @@ -1229,11 +1239,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -1241,6 +1249,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- DeviceDefault theme for a presentation window on a secondary display. --> @@ -1321,11 +1332,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -1333,6 +1342,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- DeviceDefault theme for panel windows. This removes all extraneous window @@ -1415,11 +1427,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -1427,6 +1437,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear @@ -1508,11 +1521,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -1520,6 +1531,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear @@ -1601,11 +1615,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -1613,6 +1625,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- DeviceDefault style for input methods, which is used by the @@ -1694,11 +1709,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -1706,6 +1719,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- DeviceDefault style for input methods, which is used by the @@ -1787,11 +1803,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -1799,6 +1813,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert"> @@ -1880,11 +1897,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -1892,6 +1907,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- Theme for the dialog shown when an app crashes or ANRs. --> @@ -1978,11 +1996,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -1990,6 +2006,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame"> @@ -2069,11 +2088,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -2081,6 +2098,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style --> @@ -2298,11 +2318,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -2310,6 +2328,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an @@ -2391,11 +2412,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -2403,6 +2422,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar --> @@ -2483,11 +2505,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -2495,6 +2515,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar. @@ -2576,11 +2599,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -2588,6 +2609,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar @@ -2671,11 +2695,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -2683,6 +2705,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent @@ -2765,11 +2790,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -2777,6 +2800,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be @@ -2865,11 +2891,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -2877,6 +2901,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a @@ -2961,11 +2988,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -2973,6 +2998,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar --> @@ -3056,11 +3084,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3068,6 +3094,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum @@ -3152,11 +3181,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3164,6 +3191,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. --> @@ -3229,11 +3259,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3241,6 +3269,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. --> @@ -3306,11 +3337,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3318,6 +3347,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller @@ -3402,11 +3434,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3414,6 +3444,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- DeviceDefault light theme for a window without an action bar that will be displayed either @@ -3499,11 +3532,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3511,6 +3542,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- DeviceDefault light theme for a presentation window on a secondary display. --> @@ -3594,11 +3628,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3606,6 +3638,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- DeviceDefault light theme for panel windows. This removes all extraneous window @@ -3688,11 +3723,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3700,6 +3733,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert"> @@ -3781,11 +3817,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3793,6 +3827,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="Theme.DeviceDefault.Light.Dialog.Alert" /> @@ -3874,11 +3911,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3886,6 +3921,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice"> @@ -3965,11 +4003,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -3977,6 +4013,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- DeviceDefault theme for a window that should look like the Settings app. --> @@ -4063,11 +4102,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -4075,7 +4112,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> - + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <style name="Theme.DeviceDefault.SystemUI" parent="Theme.DeviceDefault.Light"> @@ -4143,11 +4182,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -4155,7 +4192,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> - + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <style name="Theme.DeviceDefault.SystemUI.Dialog" parent="Theme.DeviceDefault.Light.Dialog"> @@ -4215,11 +4254,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -4227,7 +4264,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> - + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar --> @@ -4309,11 +4348,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -4321,6 +4358,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.Light.BaseDialog"> @@ -4386,11 +4426,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -4398,7 +4436,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> - + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.DeviceDefault.Settings.DialogBase"> @@ -4504,11 +4544,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -4516,6 +4554,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert"> @@ -4599,11 +4640,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -4611,6 +4650,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" /> @@ -4720,11 +4762,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -4732,7 +4772,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> - + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <style name="ThemeOverlay.DeviceDefault.Accent.Light"> @@ -4772,11 +4814,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -4784,6 +4824,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. --> @@ -4827,11 +4870,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -4839,6 +4880,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen"> @@ -4878,11 +4922,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item> - <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorOnError">@color/system_on_error_light</item> <item name="materialColorSurface">@color/system_surface_light</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item> - <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item> <item name="materialColorOutline">@color/system_outline_light</item> @@ -4890,6 +4932,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_light</item> <item name="materialColorOnSurface">@color/system_on_surface_light</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item> + <item name="materialColorPrimary">@color/system_primary_light</item> + <item name="materialColorSecondary">@color/system_secondary_light</item> + <item name="materialColorTertiary">@color/system_tertiary_light</item> </style> <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification"> @@ -4940,11 +4985,9 @@ easier. <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item> <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item> <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item> - <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorOnError">@color/system_on_error_dark</item> <item name="materialColorSurface">@color/system_surface_dark</item> <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item> - <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item> <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item> <item name="materialColorOutline">@color/system_outline_dark</item> @@ -4952,6 +4995,9 @@ easier. <item name="materialColorOnPrimary">@color/system_on_primary_dark</item> <item name="materialColorOnSurface">@color/system_on_surface_dark</item> <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item> + <item name="materialColorPrimary">@color/system_primary_dark</item> + <item name="materialColorSecondary">@color/system_secondary_dark</item> + <item name="materialColorTertiary">@color/system_tertiary_dark</item> </style> <style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight"> <item name="colorListDivider">@color/list_divider_opacity_device_default_light</item> diff --git a/core/tests/coretests/res/drawable-nodpi/test_too_big.png b/core/tests/coretests/res/drawable-nodpi/test_too_big.png Binary files differnew file mode 100644 index 000000000000..3754072b8e31 --- /dev/null +++ b/core/tests/coretests/res/drawable-nodpi/test_too_big.png diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java index a53d57f0383c..a102b3ed9971 100644 --- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java +++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java @@ -1127,6 +1127,31 @@ public class ValueAnimatorTests { 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 a0d8dcf830e8..ba6c8fab48d4 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 @@ class FontScaleConverterFactoryTest { } } + @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 75390a282af9..5d922961aa8b 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 static com.google.common.truth.Truth.assertThat; 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 @@ public class IconTest extends AndroidTestCase { } } + 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/utiltests/src/com/android/internal/util/QuickSelectTest.java b/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java new file mode 100644 index 000000000000..1b9d2ef58c10 --- /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/Android.bp b/data/etc/Android.bp index f233c6eca13b..6a1f3f959185 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -54,6 +54,12 @@ prebuilt_etc { src: "hiddenapi-package-whitelist.xml", } +prebuilt_etc { + name: "preinstalled-packages-asl-files.xml", + sub_dir: "sysconfig", + src: "preinstalled-packages-asl-files.xml", +} + // Privapp permission whitelist files prebuilt_etc { diff --git a/data/etc/preinstalled-packages-asl-files.xml b/data/etc/preinstalled-packages-asl-files.xml new file mode 100644 index 000000000000..6b5401c921f1 --- /dev/null +++ b/data/etc/preinstalled-packages-asl-files.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- +This XML file declares which preinstalled apps have Android Security Label data by declaring the +path to the XML file containing this data. + +Example usage: + <asl-file package="com.foo.bar" path="/vendor/etc/asl/com.foo.bar.xml"/> +--> + +<config></config> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 0faf62e03b14..40cb7f2194e9 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -122,6 +122,7 @@ applications that come with the platform <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 @@ applications that come with the platform <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 a76d74edc0f4..708feeb9e421 100644 --- a/graphics/java/android/graphics/drawable/Icon.java +++ b/graphics/java/android/graphics/drawable/Icon.java @@ -35,6 +35,7 @@ import android.graphics.Bitmap; 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 @@ import java.util.Objects; 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 @@ public final class Icon implements Parcelable { } /** + * 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 @@ public final class Icon implements Parcelable { } } 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 @@ public final class Icon implements Parcelable { } 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/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index ffd041f60e26..fe5432fcd9d8 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -320,6 +320,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final boolean mCriticalToDeviceEncryption; private final int mMaxUsageCount; private final String mAttestKeyAlias; + private final long mBoundToSecureUserId; + /* * ***NOTE***: All new fields MUST also be added to the following: * ParcelableKeyGenParameterSpec class. @@ -362,7 +364,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu boolean unlockedDeviceRequired, boolean criticalToDeviceEncryption, int maxUsageCount, - String attestKeyAlias) { + String attestKeyAlias, + long boundToSecureUserId) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -422,6 +425,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mCriticalToDeviceEncryption = criticalToDeviceEncryption; mMaxUsageCount = maxUsageCount; mAttestKeyAlias = attestKeyAlias; + mBoundToSecureUserId = boundToSecureUserId; } /** @@ -842,10 +846,20 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Return the secure user id that this key should be bound to. + * + * Normally an authentication-bound key is tied to the secure user id of the current user + * (either the root SID from GateKeeper for auth-bound keys with a timeout, or the authenticator + * id of the current biometric set for keys requiring explicit biometric authorization). + * If this parameter is set (this method returning non-zero value), the key should be tied to + * the specified secure user id, overriding the logic above. + * + * This is only applicable when {@link #isUserAuthenticationRequired} is {@code true} + * * @hide */ public long getBoundToSpecificSecureUserId() { - return GateKeeper.INVALID_SECURE_USER_ID; + return mBoundToSecureUserId; } /** @@ -920,6 +934,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private boolean mCriticalToDeviceEncryption = false; private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT; private String mAttestKeyAlias = null; + private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; /** * Creates a new instance of the {@code Builder}. @@ -990,6 +1005,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption(); mMaxUsageCount = sourceSpec.getMaxUsageCount(); mAttestKeyAlias = sourceSpec.getAttestKeyAlias(); + mBoundToSecureUserId = sourceSpec.getBoundToSpecificSecureUserId(); } /** @@ -1725,6 +1741,27 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Set the secure user id that this key should be bound to. + * + * Normally an authentication-bound key is tied to the secure user id of the current user + * (either the root SID from GateKeeper for auth-bound keys with a timeout, or the + * authenticator id of the current biometric set for keys requiring explicit biometric + * authorization). If this parameter is set (this method returning non-zero value), the key + * should be tied to the specified secure user id, overriding the logic above. + * + * This is only applicable when {@link #setUserAuthenticationRequired} is set to + * {@code true} + * + * @see KeyGenParameterSpec#getBoundToSpecificSecureUserId() + * @hide + */ + @NonNull + public Builder setBoundToSpecificSecureUserId(long secureUserId) { + mBoundToSecureUserId = secureUserId; + return this; + } + + /** * Builds an instance of {@code KeyGenParameterSpec}. */ @NonNull @@ -1762,7 +1799,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUnlockedDeviceRequired, mCriticalToDeviceEncryption, mMaxUsageCount, - mAttestKeyAlias); + mAttestKeyAlias, + mBoundToSecureUserId); } } } diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java index a6e33664f2b1..9356eb85bd8a 100644 --- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java @@ -111,6 +111,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { out.writeBoolean(mSpec.isCriticalToDeviceEncryption()); out.writeInt(mSpec.getMaxUsageCount()); out.writeString(mSpec.getAttestKeyAlias()); + out.writeLong(mSpec.getBoundToSpecificSecureUserId()); } private static Date readDateOrNull(Parcel in) { @@ -172,6 +173,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { final boolean criticalToDeviceEncryption = in.readBoolean(); final int maxUsageCount = in.readInt(); final String attestKeyAlias = in.readString(); + final long boundToSecureUserId = in.readLong(); // The KeyGenParameterSpec is intentionally not constructed using a Builder here: // The intention is for this class to break if new parameters are added to the // KeyGenParameterSpec constructor (whereas using a builder would silently drop them). @@ -208,7 +210,8 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { unlockedDeviceRequired, criticalToDeviceEncryption, maxUsageCount, - attestKeyAlias); + attestKeyAlias, + boundToSecureUserId); } public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() { diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml index dcce4698c252..ab64f9e359b0 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/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 47d3a5c52074..dc27ceb7f51c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -20,6 +20,9 @@ import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTas import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityTaskManager; @@ -37,7 +40,9 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings.Global; +import android.util.DisplayMetrics; import android.util.Log; +import android.util.MathUtils; import android.util.SparseArray; import android.view.IRemoteAnimationRunner; import android.view.InputDevice; @@ -56,6 +61,7 @@ import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.view.AppearanceRegion; +import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; @@ -80,6 +86,17 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont public static boolean IS_U_ANIMATION_ENABLED = SystemProperties.getInt("persist.wm.debug.predictive_back_anim", SETTING_VALUE_ON) == SETTING_VALUE_ON; + + public static final float FLING_MAX_LENGTH_SECONDS = 0.1f; // 100ms + public static final float FLING_SPEED_UP_FACTOR = 0.6f; + + /** + * The maximum additional progress in case of fling gesture. + * The end animation starts after the user lifts the finger from the screen, we continue to + * fire {@link BackEvent}s until the velocity reaches 0. + */ + private static final float MAX_FLING_PROGRESS = 0.3f; /* 30% of the screen */ + /** Predictive back animation developer option */ private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); /** @@ -96,6 +113,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean mShouldStartOnNextMoveEvent = false; /** @see #setTriggerBack(boolean) */ private boolean mTriggerBack; + private FlingAnimationUtils mFlingAnimationUtils; @Nullable private BackNavigationInfo mBackNavigationInfo; @@ -174,6 +192,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + mFlingAnimationUtils = new FlingAnimationUtils.Builder(displayMetrics) + .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS) + .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) + .build(); } @VisibleForTesting @@ -465,6 +488,78 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } + + /** + * Allows us to manage the fling gesture, it smoothly animates the current progress value to + * the final position, calculated based on the current velocity. + * + * @param callback the callback to be invoked when the animation ends. + */ + private void dispatchOrAnimateOnBackInvoked(IOnBackInvokedCallback callback) { + if (callback == null) { + return; + } + + boolean animationStarted = false; + + if (mBackNavigationInfo != null && mBackNavigationInfo.isAnimationCallback()) { + + final BackMotionEvent backMotionEvent = mTouchTracker.createProgressEvent(); + if (backMotionEvent != null) { + // Constraints - absolute values + float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond(); + float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond(); + float maxX = mTouchTracker.getMaxX(); // px + float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px + + // Current state + float currentX = backMotionEvent.getTouchX(); + float velocity = MathUtils.constrain(backMotionEvent.getVelocityX(), + -maxVelocity, maxVelocity); + + // Target state + float animationFaction = velocity / maxVelocity; // value between -1 and 1 + float flingDistance = animationFaction * maxFlingDistance; // px + float endX = MathUtils.constrain(currentX + flingDistance, 0f, maxX); + + if (!Float.isNaN(endX) + && currentX != endX + && Math.abs(velocity) >= minVelocity) { + ValueAnimator animator = ValueAnimator.ofFloat(currentX, endX); + + mFlingAnimationUtils.apply( + /* animator = */ animator, + /* currValue = */ currentX, + /* endValue = */ endX, + /* velocity = */ velocity, + /* maxDistance = */ maxFlingDistance + ); + + animator.addUpdateListener(animation -> { + Float animatedValue = (Float) animation.getAnimatedValue(); + float progress = mTouchTracker.getProgress(animatedValue); + final BackMotionEvent backEvent = mTouchTracker + .createProgressEvent(progress); + dispatchOnBackProgressed(mActiveCallback, backEvent); + }); + + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dispatchOnBackInvoked(callback); + } + }); + animator.start(); + animationStarted = true; + } + } + } + + if (!animationStarted) { + dispatchOnBackInvoked(callback); + } + } + private void dispatchOnBackInvoked(IOnBackInvokedCallback callback) { if (callback == null) { return; @@ -530,7 +625,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (mBackNavigationInfo != null) { final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback(); if (mTriggerBack) { - dispatchOnBackInvoked(callback); + dispatchOrAnimateOnBackInvoked(callback); } else { dispatchOnBackCancelled(callback); } @@ -605,7 +700,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont // The next callback should be {@link #onBackAnimationFinished}. if (mTriggerBack) { - dispatchOnBackInvoked(mActiveCallback); + dispatchOrAnimateOnBackInvoked(mActiveCallback); } else { dispatchOnBackCancelled(mActiveCallback); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java index 904574b08562..7a00f5b9bab4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java @@ -16,7 +16,10 @@ package com.android.wm.shell.back; +import android.annotation.FloatRange; import android.os.SystemProperties; +import android.util.MathUtils; +import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.window.BackEvent; import android.window.BackMotionEvent; @@ -99,28 +102,42 @@ class TouchTracker { } BackMotionEvent createProgressEvent() { - float progressThreshold = PROGRESS_THRESHOLD >= 0 - ? PROGRESS_THRESHOLD : mProgressThreshold; - progressThreshold = progressThreshold == 0 ? 1 : progressThreshold; float progress = 0; // Progress is always 0 when back is cancelled and not restarted. if (!mCancelled) { - // If back is committed, progress is the distance between the last and first touch - // point, divided by the max drag distance. Otherwise, it's the distance between - // the last touch point and the starting threshold, divided by max drag distance. - // The starting threshold is initially the first touch location, and updated to - // the location everytime back is restarted after being cancelled. - float startX = mTriggerBack ? mInitTouchX : mStartThresholdX; - float deltaX = Math.max( - mSwipeEdge == BackEvent.EDGE_LEFT - ? mLatestTouchX - startX - : startX - mLatestTouchX, - 0); - progress = Math.min(Math.max(deltaX / progressThreshold, 0), 1); + progress = getProgress(mLatestTouchX); } return createProgressEvent(progress); } + /** + * Progress value computed from the touch position. + * + * @param touchX the X touch position of the {@link MotionEvent}. + * @return progress value + */ + @FloatRange(from = 0.0, to = 1.0) + float getProgress(float touchX) { + // If back is committed, progress is the distance between the last and first touch + // point, divided by the max drag distance. Otherwise, it's the distance between + // the last touch point and the starting threshold, divided by max drag distance. + // The starting threshold is initially the first touch location, and updated to + // the location everytime back is restarted after being cancelled. + float startX = mTriggerBack ? mInitTouchX : mStartThresholdX; + float deltaX = Math.abs(startX - touchX); + float maxX = getMaxX(); + maxX = maxX == 0 ? 1 : maxX; + return MathUtils.constrain(deltaX / maxX, 0, 1); + } + + /** + * Maximum X value (in pixels). + * Progress is considered to be completed (1f) when this limit is exceeded. + */ + float getMaxX() { + return PROGRESS_THRESHOLD >= 0 ? PROGRESS_THRESHOLD : mProgressThreshold; + } + BackMotionEvent createProgressEvent(float progress) { return new BackMotionEvent( /* touchX = */ mLatestTouchX, 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 79e0a4868cae..d3f395846894 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 @@ import java.util.concurrent.Executor; 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 @@ public class Bubble implements BubbleViewProvider { 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 @@ public class Bubble implements BubbleViewProvider { 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 @@ public class Bubble implements BubbleViewProvider { 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 @@ public class Bubble implements BubbleViewProvider { } 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 c407b0624985..21f02b10035b 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.INVISIBLE; 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 @@ public class BubbleController implements ConfigurationChangeListener, 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 @@ public class BubbleController implements ConfigurationChangeListener, } } 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 @@ public class BubbleController implements ConfigurationChangeListener, } /** 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 @@ public class BubbleController implements ConfigurationChangeListener, 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 @@ public class BubbleController implements ConfigurationChangeListener, 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 @@ public class BubbleController implements ConfigurationChangeListener, pw.println(" suppressing: " + key); } - pw.print("mAppBubbleTaskId: " + mAppBubbleTaskId); + pw.print("mAppBubbleTaskIds: " + mAppBubbleTaskIds.values()); } } @@ -2205,7 +2206,7 @@ public class BubbleController implements ConfigurationChangeListener, @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 92b969bb6f97..cc8f50e09fcb 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 @@ package com.android.wm.shell.bubbles; 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 @@ public class BubbleData { || !(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 684a23a198c2..6c482c831152 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 @@ public class BubbleExpandedView extends LinearLayout { // 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 4970fa0cb087..56616cb6bd88 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 @@ public class SplitDecorManager extends WindowlessWindowManager { t.remove(mGapBackgroundLeash); mGapBackgroundLeash = null; } + if (mScreenshot != null) { + t.remove(mScreenshot); + mScreenshot = null; + } mHostLeash = null; mIcon = null; mResizingIconView = null; @@ -324,6 +328,8 @@ public class SplitDecorManager extends WindowlessWindowManager { 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 @@ public class SplitDecorManager extends WindowlessWindowManager { 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 d04ce1540980..b6216b340b38 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } if (Transitions.ENABLE_SHELL_TRANSITIONS) { + mPipTransitionController.onFixedRotationFinished(); clearWaitForFixedRotation(); return; } @@ -1527,6 +1532,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 5755a10897af..99cb6f782d01 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 @@ public class PipTransition extends PipTransitionController { @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 ff7ab8ba1d97..949d6f558c32 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 abstract class PipTransitionController implements Transitions.TransitionH 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 222307fba8c2..5f6b3fe1e250 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 @@ import java.util.Objects; 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 bca27a5c6636..977aad4a898a 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 @@ public class TvPipCustomAction extends TvPipAction { 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 6eef22562caa..f86f987039ba 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 android.view.ViewGroup.LayoutParams.MATCH_PARENT; 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 @@ class TvPipMenuEduTextDrawer extends FrameLayout { 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 @@ class TvPipMenuEduTextDrawer extends FrameLayout { .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.onCloseEduTextAnimationStart(); + } - mListener.onCloseEduText(); + public void onCloseEduTextAnimationEnd() { + setVisibility(GONE); + mListener.onCloseEduTextAnimationEnd(); } /** @@ -270,11 +296,8 @@ class TvPipMenuEduTextDrawer extends FrameLayout { * 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 6eb719ba60a3..d07641892552 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 @@ import java.util.List; * 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 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L private final View mDimLayer; private final TvPipMenuEduTextDrawer mEduTextDrawer; + private final ViewGroup mEduTextContainer; private final int mPipMenuOuterSpace; private final int mPipMenuBorderWidth; @@ -139,9 +141,9 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L 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 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L * 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 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L 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 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L } @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 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L } } - interface Listener extends TvPipMenuEduTextDrawer.Listener { + interface Listener { void onBackPress(); @@ -573,5 +588,11 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L * 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 c9b3a1af6507..c4a0e9cf5a74 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 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { 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 44d7e6de8ec4..b55487258220 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.TransitionRequestInfo; 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mixer.setRecentsTransition(transition); } if (transition == null) { - controller.cancel(); + controller.cancel("startRecentsTransition"); return; } controller.setTransition(transition); @@ -127,6 +131,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { && 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { + " 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { + foundRecentsClosing); if (foundRecentsClosing) { mWillFinishToHome = false; - cancel(false /* toHome */); + cancel(false /* toHome */, "didn't merge"); } return; } @@ -552,13 +592,15 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { @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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { @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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { @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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { */ @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/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index bdb7d44bad32..08b0bf74f413 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -429,6 +429,7 @@ public class Transitions implements RemoteCallable<Transitions> { && (change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) { t.setAlpha(leash, 0.f); } + finishT.show(leash); } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { finishT.hide(leash); } 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 8e8facadfd5d..e8a6a159cb19 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 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { 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 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { 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 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { 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 dfde7e6feff5..116af7094e13 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 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL @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 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL 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 e137bc462607..49a5eac5fd07 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 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { 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 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { 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 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { 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 67e99d73b811..af3fb0ead55c 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 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @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 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin 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 8b35694ff8fd..9a1b4ffbd50c 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 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> 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 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> .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 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mCaptionX; int mCaptionY; + boolean mApplyStartTransactionOnDraw; + void reset() { mLayoutResId = Resources.ID_NULL; mCaptionHeightId = Resources.ID_NULL; @@ -419,6 +427,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionX = 0; mCaptionY = 0; + + mApplyStartTransactionOnDraw = false; } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index c416ad011c4e..45024f387c25 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -223,15 +223,6 @@ fun FlickerTest.splitAppLayerBoundsChanges( portraitPosTop, scenario.endRotation ) - .then() - .isInvisible(component) - .then() - .splitAppLayerBoundsSnapToDivider( - component, - landscapePosLeft, - portraitPosTop, - scenario.endRotation - ) } else { splitAppLayerBoundsSnapToDivider( component, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt index 62936e0f5ca8..625987a2e7ef 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt @@ -293,7 +293,7 @@ internal object SplitScreenUtils { wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace ?: error("Display not found") val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS) - dividerBar.drag(Point(displayBounds.width * 1 / 3, displayBounds.height * 2 / 3), 2000) + dividerBar.drag(Point(displayBounds.width * 1 / 3, displayBounds.height * 2 / 3), 200) wmHelper .StateSyncBuilder() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index d95c7a488ea1..3d8bd3854a45 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -135,12 +135,15 @@ public class BackAnimationControllerTest extends ShellTestCase { mShellExecutor.flushAll(); } - private void createNavigationInfo(int backType, boolean enableAnimation) { + private void createNavigationInfo(int backType, + boolean enableAnimation, + boolean isAnimationCallback) { BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder() .setType(backType) .setOnBackNavigationDone(new RemoteCallback((bundle) -> {})) .setOnBackInvokedCallback(mAppCallback) - .setPrepareRemoteAnimation(enableAnimation); + .setPrepareRemoteAnimation(enableAnimation) + .setAnimationCallback(isAnimationCallback); createNavigationInfo(builder); } @@ -218,7 +221,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void backToHome_dispatchesEvents() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); - createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, + /* enableAnimation = */ true, + /* isAnimationCallback = */ false); doMotionEvent(MotionEvent.ACTION_DOWN, 0); @@ -240,6 +245,32 @@ public class BackAnimationControllerTest extends ShellTestCase { } @Test + public void backToHomeWithAnimationCallback_dispatchesEvents() throws RemoteException { + registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, + /* enableAnimation = */ true, + /* isAnimationCallback = */ true); + + doMotionEvent(MotionEvent.ACTION_DOWN, 0); + + // Check that back start and progress is dispatched when first move. + doMotionEvent(MotionEvent.ACTION_MOVE, 100, 3000); + + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); + + verify(mAnimatorCallback).onBackStarted(any(BackMotionEvent.class)); + verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any()); + ArgumentCaptor<BackMotionEvent> backEventCaptor = + ArgumentCaptor.forClass(BackMotionEvent.class); + verify(mAnimatorCallback, atLeastOnce()).onBackProgressed(backEventCaptor.capture()); + + // Check that back invocation is dispatched. + mController.setTriggerBack(true); // Fake trigger back + doMotionEvent(MotionEvent.ACTION_UP, 0); + verify(mAnimatorCallback).onBackInvoked(); + } + + @Test public void animationDisabledFromSettings() throws RemoteException { // Toggle the setting off Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0"); @@ -254,7 +285,9 @@ public class BackAnimationControllerTest extends ShellTestCase { ArgumentCaptor<BackMotionEvent> backEventCaptor = ArgumentCaptor.forClass(BackMotionEvent.class); - createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, false); + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, + /* enableAnimation = */ false, + /* isAnimationCallback = */ false); triggerBackGesture(); releaseBackGesture(); @@ -271,7 +304,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void ignoresGesture_transitionInProgress() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); - createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, + /* enableAnimation = */ true, + /* isAnimationCallback = */ false); triggerBackGesture(); simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); @@ -309,7 +344,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void acceptsGesture_transitionTimeout() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); - createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, + /* enableAnimation = */ true, + /* isAnimationCallback = */ false); // In case it is still running in animation. doNothing().when(mAnimatorCallback).onBackInvoked(); @@ -334,7 +371,9 @@ public class BackAnimationControllerTest extends ShellTestCase { public void cancelBackInvokeWhenLostFocus() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); - createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, + /* enableAnimation = */ true, + /* isAnimationCallback = */ false); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. @@ -454,7 +493,9 @@ public class BackAnimationControllerTest extends ShellTestCase { mController.registerAnimation(type, animationRunner); - createNavigationInfo(type, true); + createNavigationInfo(type, + /* enableAnimation = */ true, + /* isAnimationCallback = */ false); doMotionEvent(MotionEvent.ACTION_DOWN, 0); @@ -473,11 +514,15 @@ public class BackAnimationControllerTest extends ShellTestCase { } private void doMotionEvent(int actionDown, int coordinate) { + doMotionEvent(actionDown, coordinate, 0); + } + + private void doMotionEvent(int actionDown, int coordinate, float velocity) { mController.onMotionEvent( /* touchX */ coordinate, /* touchY */ coordinate, - /* velocityX = */ 0, - /* velocityY = */ 0, + /* velocityX = */ velocity, + /* velocityY = */ velocity, /* keyAction */ actionDown, /* swipeEdge */ BackEvent.EDGE_LEFT); } 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 919bf0665b5e..4a55429eacb6 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 @@ public class BubbleDataTest extends ShellTestCase { 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 @@ public class BubbleDataTest extends ShellTestCase { @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 c1e53a90b7e0..fc4bfd9754a0 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.mock; 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.Point; 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 @@ public class WindowDecorationTests extends ShellTestCase { @Mock private SurfaceControlViewHost mMockSurfaceControlViewHost; @Mock + private AttachedSurfaceControl mMockRootSurfaceControl; + @Mock private TestView mMockView; @Mock private WindowContainerTransaction mMockWindowContainerTransaction; @@ -129,6 +133,8 @@ public class WindowDecorationTests extends ShellTestCase { doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory) .create(any(), any(), any()); + when(mMockSurfaceControlViewHost.getRootSurfaceControl()) + .thenReturn(mMockRootSurfaceControl); } @Test @@ -461,6 +467,43 @@ public class WindowDecorationTests extends ShellTestCase { 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 @@ public class WindowDecorationTests extends ShellTestCase { @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 70c36a5803ee..34af1f9ea039 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -399,7 +399,7 @@ cc_defaults { "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 8874ef1d2fe0..69418b09fee6 100644 --- a/libs/hwui/jni/YuvToJpegEncoder.cpp +++ b/libs/hwui/jni/YuvToJpegEncoder.cpp @@ -298,39 +298,39 @@ void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { } /////////////////////////////////////////////////////////////////////////////// -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 @@ bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env, 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 d22a26c83567..8ef780547184 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 @@ public: * @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 @@ public: * @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/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java index 5f5e214357ea..094a33f3f2ba 100644 --- a/media/java/android/media/audiopolicy/AudioMix.java +++ b/media/java/android/media/audiopolicy/AudioMix.java @@ -25,6 +25,8 @@ import android.media.AudioFormat; import android.media.AudioSystem; import android.os.Build; +import com.android.internal.annotations.VisibleForTesting; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; @@ -252,10 +254,10 @@ public class AudioMix { if (o == null || getClass() != o.getClass()) return false; final AudioMix that = (AudioMix) o; - return (mRouteFlags == that.mRouteFlags) - && (mMixType == that.mMixType) - && Objects.equals(mRule, that.mRule) - && Objects.equals(mFormat, that.mFormat); + return Objects.equals(this.mRouteFlags, that.mRouteFlags) + && Objects.equals(this.mRule, that.mRule) + && Objects.equals(this.mMixType, that.mMixType) + && Objects.equals(this.mFormat, that.mFormat); } /** @hide */ @@ -340,7 +342,8 @@ public class AudioMix { * @param address * @return the same Builder instance. */ - Builder setDevice(int deviceType, String address) { + @VisibleForTesting + public Builder setDevice(int deviceType, String address) { mDeviceSystemType = deviceType; mDeviceAddress = address; return this; diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 7e1bbe3dc5ed..29e8716f08ac 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -293,12 +293,16 @@ public final class MediaSession { * 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/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java index 556a05c55f88..d2fd78012193 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java @@ -121,16 +121,15 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V if (viewHolder.mExpandButton.getTag() == null) { viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more); } - // Add expand buttons if the permissions are more than PERMISSION_SIZE in this list. + // Add expand buttons if the permissions are more than PERMISSION_SIZE in this list also + // make the summary invisible by default. if (mPermissions.size() > PERMISSION_SIZE) { + + viewHolder.mPermissionSummary.setVisibility(View.GONE); + view.setOnClickListener(v -> { if ((Integer) viewHolder.mExpandButton.getTag() == R.drawable.btn_expand_more) { viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_less); - - if (viewHolder.mSummary != null) { - viewHolder.mPermissionSummary.setText(viewHolder.mSummary); - } - viewHolder.mPermissionSummary.setVisibility(View.VISIBLE); viewHolder.mExpandButton.setTag(R.drawable.btn_expand_less); } else { @@ -139,6 +138,11 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more); } }); + } else { + // Remove expand buttons if the permissions are less than PERMISSION_SIZE in this list + // also show the summary by default. + viewHolder.mPermissionSummary.setVisibility(View.VISIBLE); + viewHolder.mExpandButton.setVisibility(View.GONE); } return viewHolder; @@ -150,15 +154,8 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V final Spanned title = getHtmlFromResources(mContext, sTitleMap.get(type)); final Spanned summary = getHtmlFromResources(mContext, sSummaryMap.get(type)); - holder.mSummary = summary; + holder.mPermissionSummary.setText(summary); holder.mPermissionName.setText(title); - - if (mPermissions.size() <= PERMISSION_SIZE) { - holder.mPermissionSummary.setText(summary); - holder.mExpandButton.setVisibility(View.GONE); - } else { - holder.mPermissionSummary.setVisibility(View.GONE); - } } @Override @@ -181,7 +178,6 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V private final TextView mPermissionSummary; private final ImageView mPermissionIcon; private final ImageButton mExpandButton; - private Spanned mSummary = null; ViewHolder(View itemView) { super(itemView); mPermissionName = itemView.findViewById(R.id.permission_name); diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt index 7581b5c0aa91..8b9c8b9a768f 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.common.ui.Snackbar 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 @@ class CredentialSelectorActivity : ComponentActivity() { 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/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt index 4d2bb4c6016a..8b74d766a152 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt @@ -18,6 +18,7 @@ package com.android.credentialmanager import android.app.Activity import android.os.IBinder +import android.text.TextUtils import android.util.Log import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult @@ -67,9 +68,9 @@ class CredentialSelectorViewModel( var uiMetrics: UIMetrics = UIMetrics() - init{ + init { uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_INIT, - credManRepo.requestInfo?.appPackageName) + credManRepo.requestInfo?.appPackageName) } /**************************************************************************/ @@ -100,7 +101,7 @@ class CredentialSelectorViewModel( if (this.credManRepo.requestInfo?.token != credManRepo.requestInfo?.token) { this.uiMetrics.resetInstanceId() this.uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_NEW_REQUEST, - credManRepo.requestInfo?.appPackageName) + credManRepo.requestInfo?.appPackageName) } } @@ -174,7 +175,7 @@ class CredentialSelectorViewModel( private fun onInternalError() { Log.w(Constants.LOG_TAG, "UI closed due to illegal internal state") this.uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_INTERNAL_ERROR, - credManRepo.requestInfo?.appPackageName) + credManRepo.requestInfo?.appPackageName) credManRepo.onParsingFailureCancel() uiState = uiState.copy(dialogState = DialogState.COMPLETE) } @@ -314,10 +315,11 @@ class CredentialSelectorViewModel( uiState = uiState.copy( createCredentialUiState = uiState.createCredentialUiState?.copy( currentScreenState = - if (activeEntry.activeProvider.id == - userConfigRepo.getDefaultProviderId()) + if (activeEntry.activeProvider.id == userConfigRepo.getDefaultProviderId() || + !TextUtils.isEmpty(uiState.createCredentialUiState?.requestDisplayInfo + ?.appPreferredDefaultProviderId)) CreateScreenState.CREATION_OPTION_SELECTION - else CreateScreenState.MORE_OPTIONS_ROW_INTRO, + else CreateScreenState.DEFAULT_PROVIDER_CONFIRMATION, activeEntry = activeEntry ) ) diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index f08bbf430440..57035d426654 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -50,9 +50,7 @@ import com.android.credentialmanager.getflow.TopBrandingContent 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 @@ class GetFlowUtils { 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/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 96010cc66821..9d871ed75494 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -139,12 +139,12 @@ fun CreateCredentialScreen( onRemoteEntrySelected = viewModel::createFlowOnEntrySelected, onLog = { viewModel.logUiEvent(it) }, ) - CreateScreenState.MORE_OPTIONS_ROW_INTRO -> { + CreateScreenState.DEFAULT_PROVIDER_CONFIRMATION -> { if (createCredentialUiState.activeEntry == null) { viewModel.onIllegalUiState("Expect active entry to be non-null" + " upon default provider dialog.") } else { - MoreOptionsRowIntroCard( + DefaultProviderConfirmationCard( selectedEntry = createCredentialUiState.activeEntry, onIllegalScreenState = viewModel::onIllegalUiState, onChangeDefaultSelected = @@ -420,7 +420,7 @@ fun MoreOptionsSelectionCard( } @Composable -fun MoreOptionsRowIntroCard( +fun DefaultProviderConfirmationCard( selectedEntry: ActiveEntry, onIllegalScreenState: (String) -> Unit, onChangeDefaultSelected: () -> Unit, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt index 12bb6298b282..225dbf2b744f 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt @@ -126,6 +126,6 @@ enum class CreateScreenState { PROVIDER_SELECTION, CREATION_OPTION_SELECTION, MORE_OPTIONS_SELECTION, - MORE_OPTIONS_ROW_INTRO, + DEFAULT_PROVIDER_CONFIRMATION, EXTERNAL_ONLY_SELECTION, } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index 516c1a3bb1e3..74933c9e3da6 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 @@ fun PrimarySelectionCard( 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 57fefbe577b4..000000000000 --- 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 a4a163bbabc3..716f47450ae9 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 @@ internal fun hasContentToDisplay(state: GetCredentialUiState): Boolean { !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/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java index cb386fbff4ed..7a26f76894cd 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java @@ -97,7 +97,6 @@ public class BatterySaverUtilsTest { assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true, SAVER_ENABLED_UNKNOWN)).isTrue(); - verify(mMockContext, times(0)).sendBroadcast(any(Intent.class)); verify(mMockPowerManager, times(1)).setPowerSaveModeEnabled(eq(true)); assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1)); @@ -117,7 +116,6 @@ public class BatterySaverUtilsTest { assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true, SAVER_ENABLED_UNKNOWN)).isTrue(); - verify(mMockContext, times(0)).sendBroadcast(any(Intent.class)); verify(mMockPowerManager, times(1)).setPowerSaveModeEnabled(eq(true)); assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1)); @@ -136,7 +134,6 @@ public class BatterySaverUtilsTest { assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, false, SAVER_ENABLED_UNKNOWN)).isTrue(); - verify(mMockContext, times(0)).sendBroadcast(any(Intent.class)); verify(mMockPowerManager, times(1)).setPowerSaveModeEnabled(eq(true)); assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1)); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 78d93bd19a15..751fbaad2abe 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/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java index 96ea5b45282b..27aade5e6bf8 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java @@ -19,6 +19,7 @@ package com.android.systemui.accessibility.accessibilitymenu; import android.Manifest; import android.accessibilityservice.AccessibilityButtonController; import android.accessibilityservice.AccessibilityService; +import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -82,6 +83,9 @@ public class AccessibilityMenuService extends AccessibilityService // TODO(b/136716947): Support multi-display once a11y framework side is ready. private DisplayManager mDisplayManager; + + private KeyguardManager mKeyguardManager; + private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { int mRotation; @@ -114,7 +118,7 @@ public class AccessibilityMenuService extends AccessibilityService private final BroadcastReceiver mToggleMenuReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - mA11yMenuLayout.toggleVisibility(); + toggleVisibility(); } }; @@ -159,10 +163,7 @@ public class AccessibilityMenuService extends AccessibilityService */ @Override public void onClicked(AccessibilityButtonController controller) { - if (SystemClock.uptimeMillis() - mLastTimeTouchedOutside - > BUTTON_CLICK_TIMEOUT) { - mA11yMenuLayout.toggleVisibility(); - } + toggleVisibility(); } /** @@ -209,6 +210,7 @@ public class AccessibilityMenuService extends AccessibilityService mDisplayManager = getSystemService(DisplayManager.class); mDisplayManager.registerDisplayListener(mDisplayListener, null); mAudioManager = getSystemService(AudioManager.class); + mKeyguardManager = getSystemService(KeyguardManager.class); sInitialized = true; } @@ -379,4 +381,12 @@ public class AccessibilityMenuService extends AccessibilityService } return false; } + + private void toggleVisibility() { + boolean locked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); + if (!locked && SystemClock.uptimeMillis() - mLastTimeTouchedOutside + > BUTTON_CLICK_TIMEOUT) { + mA11yMenuLayout.toggleVisibility(); + } + } } diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java index 7277392f1841..9d1af0e2375a 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java @@ -33,6 +33,7 @@ import static com.google.common.truth.Truth.assertThat; import android.accessibilityservice.AccessibilityServiceInfo; import android.app.Instrumentation; +import android.app.KeyguardManager; import android.app.UiAutomation; import android.content.BroadcastReceiver; import android.content.Context; @@ -44,6 +45,7 @@ import android.media.AudioManager; import android.os.PowerManager; import android.provider.Settings; import android.util.Log; +import android.view.Display; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -69,15 +71,19 @@ public class AccessibilityMenuServiceTest { private static final int CLICK_ID = AccessibilityNodeInfo.ACTION_CLICK; private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5; - private static final int TIMEOUT_UI_CHANGE_S = 10; + private static final int TIMEOUT_UI_CHANGE_S = 5; private static final int NO_GLOBAL_ACTION = -1; private static final String INPUT_KEYEVENT_KEYCODE_BACK = "input keyevent KEYCODE_BACK"; + private static final String TEST_PIN = "1234"; private static Instrumentation sInstrumentation; private static UiAutomation sUiAutomation; private static AtomicInteger sLastGlobalAction; private static AccessibilityManager sAccessibilityManager; + private static PowerManager sPowerManager; + private static KeyguardManager sKeyguardManager; + private static DisplayManager sDisplayManager; @BeforeClass public static void classSetup() throws Throwable { @@ -85,8 +91,14 @@ public class AccessibilityMenuServiceTest { sInstrumentation = InstrumentationRegistry.getInstrumentation(); sUiAutomation = sInstrumentation.getUiAutomation( UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); + sUiAutomation.adoptShellPermissionIdentity( + UiAutomation.ALL_PERMISSIONS.toArray(new String[0])); + final Context context = sInstrumentation.getTargetContext(); sAccessibilityManager = context.getSystemService(AccessibilityManager.class); + sPowerManager = context.getSystemService(PowerManager.class); + sKeyguardManager = context.getSystemService(KeyguardManager.class); + sDisplayManager = context.getSystemService(DisplayManager.class); // Disable all a11yServices if any are active. if (!sAccessibilityManager.getEnabledAccessibilityServiceList( @@ -123,12 +135,16 @@ public class AccessibilityMenuServiceTest { @AfterClass public static void classTeardown() throws Throwable { + clearPin(); Settings.Secure.putString(sInstrumentation.getTargetContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, ""); } @Before public void setup() throws Throwable { + clearPin(); + wakeUpScreen(); + sUiAutomation.executeShellCommand("input keyevent KEYCODE_MENU"); openMenu(); } @@ -138,11 +154,43 @@ public class AccessibilityMenuServiceTest { sLastGlobalAction.set(NO_GLOBAL_ACTION); } + private static void clearPin() throws Throwable { + sUiAutomation.executeShellCommand("locksettings clear --old " + TEST_PIN); + TestUtils.waitUntil("Device did not register as unlocked & insecure.", + TIMEOUT_SERVICE_STATUS_CHANGE_S, + () -> !sKeyguardManager.isDeviceSecure()); + } + + private static void setPin() throws Throwable { + sUiAutomation.executeShellCommand("locksettings set-pin " + TEST_PIN); + TestUtils.waitUntil("Device did not recognize as locked & secure.", + TIMEOUT_SERVICE_STATUS_CHANGE_S, + () -> sKeyguardManager.isDeviceSecure()); + } + private static boolean isMenuVisible() { AccessibilityNodeInfo root = sUiAutomation.getRootInActiveWindow(); return root != null && root.getPackageName().toString().equals(PACKAGE_NAME); } + private static void wakeUpScreen() throws Throwable { + sUiAutomation.executeShellCommand("input keyevent KEYCODE_WAKEUP"); + TestUtils.waitUntil("Screen did not wake up.", + TIMEOUT_UI_CHANGE_S, + () -> sPowerManager.isInteractive()); + } + + private static void closeScreen() throws Throwable { + Display display = sDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); + setPin(); + sUiAutomation.performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN); + TestUtils.waitUntil("Screen did not close.", + TIMEOUT_UI_CHANGE_S, + () -> !sPowerManager.isInteractive() + && display.getState() == Display.STATE_OFF + ); + } + private static void openMenu() throws Throwable { Intent intent = new Intent(PACKAGE_NAME + INTENT_TOGGLE_MENU); sInstrumentation.getContext().sendBroadcast(intent); @@ -381,23 +429,24 @@ public class AccessibilityMenuServiceTest { @Test public void testOnScreenLock_closesMenu() throws Throwable { - openMenu(); - Context context = sInstrumentation.getTargetContext(); - PowerManager powerManager = context.getSystemService(PowerManager.class); - - assertThat(powerManager).isNotNull(); - assertThat(powerManager.isInteractive()).isTrue(); + closeScreen(); + wakeUpScreen(); - sUiAutomation.performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN); - TestUtils.waitUntil("Screen did not become locked", - TIMEOUT_UI_CHANGE_S, - () -> !powerManager.isInteractive()); + assertThat(isMenuVisible()).isFalse(); + } - sUiAutomation.executeShellCommand("input keyevent KEYCODE_WAKEUP"); - TestUtils.waitUntil("Screen did not wake up", - TIMEOUT_UI_CHANGE_S, - () -> powerManager.isInteractive()); + @Test + public void testOnScreenLock_cannotOpenMenu() throws Throwable { + closeScreen(); + wakeUpScreen(); - assertThat(isMenuVisible()).isFalse(); + boolean timedOut = false; + try { + openMenu(); + } catch (AssertionError e) { + // Expected + timedOut = true; + } + assertThat(timedOut).isTrue(); } } 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 3eb7fd84dc49..23f16f2a3137 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 @@ package com.android.systemui.animation 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 @@ class TextInterpolator(layout: Layout) { 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 1786d139e966..91c0a5b635c9 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 @@ data class RippleAnimationConfig( 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 b5b6037aeae4..7e56f4b3d2b2 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 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) : 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 ef5ad436ec38..b89912709576 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 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a 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 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a * * 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 a5f832a17de4..ff150c8cba13 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 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes } @Test - fun `No violations`() { + fun noViolations() { lint() .files( *LEGITIMATE_FILES, @@ -51,7 +51,7 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes } @Test - fun `Violation - domain depends on ui`() { + fun violation_domainDependsOnUi() { lint() .files( *LEGITIMATE_FILES, @@ -86,7 +86,7 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes } @Test - fun `Violation - ui depends on data`() { + fun violation_uiDependsOnData() { lint() .files( *LEGITIMATE_FILES, @@ -121,7 +121,7 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes } @Test - fun `Violation - shared depends on all other layers`() { + fun violation_sharedDependsOnAllOtherLayers() { lint() .files( *LEGITIMATE_FILES, @@ -166,7 +166,7 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes } @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 eaf3229bf0c8..34adcc79dd11 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.ClockSettings 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.CoroutineDispatcher 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 @@ private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut( 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 @@ open class ClockRegistry( 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 @@ open class ClockRegistry( } 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 @@ open class ClockRegistry( 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 @@ open class ClockRegistry( 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 @@ open class ClockRegistry( ) } } 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 @@ open class ClockRegistry( } 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 @@ open class ClockRegistry( 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 3ec3b5c3f758..6ca7f12e842b 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 @@ class DefaultClockController( 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 @@ class DefaultClockController( 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 @@ class DefaultClockController( view.logBuffer = value } + override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f) + internal set + init { if (seedColor != null) { currentColor = seedColor!! @@ -170,6 +170,12 @@ class DefaultClockController( 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 @@ class DefaultClockController( } } - inner class DefaultClockAnimations( + open inner class DefaultClockAnimations( + val view: AnimatableClockView, dozeFraction: Float, foldFraction: Float, ) : ClockAnimations { @@ -229,34 +236,40 @@ class DefaultClockController( 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 9b4c21efb27f..4a6240b56081 100644 --- a/packages/SystemUI/docs/dagger.md +++ b/packages/SystemUI/docs/dagger.md @@ -108,20 +108,13 @@ You can then include your module in one of three places: ### 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/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt index 204bac88bc0d..450c6166905c 100644 --- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt +++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt @@ -316,9 +316,9 @@ enum class Style(internal val coreSpec: CoreSpec) { ), CLOCK_VIBRANT( CoreSpec( - a1 = TonalSpec(HueSource(), ChromaBound(ChromaSource(), 30.0, Chroma.MAX_VALUE)), - a2 = TonalSpec(HueAdd(20.0), ChromaBound(ChromaSource(), 30.0, Chroma.MAX_VALUE)), - a3 = TonalSpec(HueAdd(60.0), ChromaBound(ChromaSource(), 30.0, Chroma.MAX_VALUE)), + a1 = TonalSpec(HueSource(), ChromaBound(ChromaSource(), 70.0, Chroma.MAX_VALUE)), + a2 = TonalSpec(HueAdd(20.0), ChromaBound(ChromaSource(), 70.0, Chroma.MAX_VALUE)), + a3 = TonalSpec(HueAdd(60.0), ChromaBound(ChromaSource(), 70.0, Chroma.MAX_VALUE)), // Not Used n1 = TonalSpec(HueSource(), ChromaConstant(0.0)), 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 05630e795476..8ef2d8033000 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 @@ interface ClockController { /** 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 @@ interface ClockController { 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 @@ interface ClockFaceController { /** 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 @@ data class ClockMetadata( /** 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 @@ data class ClockFaceConfig( /** 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 10bb00cad1a0..a8ed84393cfb 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 com.android.systemui.SystemUIInitializerImpl { *; } --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 com.android.systemui.tv.TvSystemUIInitializer { *; } --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,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); -} -# 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 000000000000..1d008cf57ea7 --- /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 26502f1c1752..bbac7b014064 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 000000000000..8f8bff86f64d --- /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 7262a736c433..8b87e2a538ca 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -99,8 +99,10 @@ constructor( 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 @@ constructor( 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 @@ constructor( 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 @@ constructor( private val configListener = object : ConfigurationController.ConfigurationListener { override fun onThemeChanged() { - clock?.events?.onColorPaletteChanged(resources) + clock?.run { events.onColorPaletteChanged(resources) } updateColors() } @@ -253,7 +259,10 @@ constructor( 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 @@ constructor( 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 @@ constructor( 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 @@ constructor( } 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 @@ constructor( 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 5ba0ad62e9fb..a6c782d88e18 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.io.PrintWriter; 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 @@ public class KeyguardClockSwitch extends RelativeLayout { 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 @@ public class KeyguardClockSwitch extends RelativeLayout { 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 @@ public class KeyguardClockSwitch extends RelativeLayout { 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 @@ public class KeyguardClockSwitch extends RelativeLayout { 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 ad333b7bfbb6..a34c9fa5aced 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.notification.stack.AnimationProperties; 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS LockscreenSmartspaceController smartspaceController, KeyguardUnlockAnimationController keyguardUnlockAnimationController, SecureSettings secureSettings, - @Main Executor uiExecutor, + @Main DelayableExecutor uiExecutor, DumpManager dumpManager, ClockEventController clockEventController, @KeyguardClockLog LogBuffer logBuffer) { @@ -344,7 +344,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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 class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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 5b0e29005d82..461d390fd477 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt @@ -41,10 +41,10 @@ data class KeyguardFaceListenModel( 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 @@ data class KeyguardFaceListenModel( 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 @@ data class KeyguardFaceListenModel( 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 @@ data class KeyguardFaceListenModel( "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 b8c0ccbd8aaa..f2685c5200ad 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt @@ -41,11 +41,11 @@ data class KeyguardFingerprintListenModel( 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 @@ data class KeyguardFingerprintListenModel( 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 @@ data class KeyguardFingerprintListenModel( 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 @@ data class KeyguardFingerprintListenModel( "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 76e051ea25f3..693268d730a4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -178,6 +178,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard @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 0cdef4d63639..edfcb8d0d1a6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -349,7 +349,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV 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 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV 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 c48aaf451e62..10c08bc3e8b3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -297,7 +297,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab || (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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab !mSwitchingUser && !fingerprintDisabledForUser && (!mKeyguardGoingAway || !mDeviceInteractive) - && mIsPrimaryUser + && mIsSystemUser && biometricEnabledForUser && !isUserInLockdown(user); final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed(); @@ -3025,11 +3021,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab isKeyguardVisible(), mKeyguardOccluded, mOccludingAppRequestingFp, - mIsPrimaryUser, shouldListenSideFpsState, shouldListenForFingerprintAssistant, strongerAuthRequired, mSwitchingUser, + mIsSystemUser, isUdfps, userDoesNotHaveTrust)); @@ -3074,7 +3070,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab || mAlternateBouncerShowing) && !mSwitchingUser && !faceDisabledForUser && userNotTrustedOrDetectionIsNeeded && !mKeyguardGoingAway && biometricEnabledForUser - && faceAuthAllowedOrDetectionIsNeeded && mIsPrimaryUser + && faceAuthAllowedOrDetectionIsNeeded && mIsSystemUser && (!mSecureCameraLaunched || mAlternateBouncerShowing) && faceAndFpNotAuthenticated && !mGoingToSleep @@ -3112,10 +3108,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab 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 cde8ff78c620..6740375c4329 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.Background; 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 @@ public abstract class ClockRegistryModule { @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 @@ public abstract class ClockRegistryModule { /* 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 b6ee4cbc7641..3349fe5f1147 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.ComponentCallbacks; 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 @@ public class MagnificationSettingsController implements ComponentCallbacks { @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 71c5f247b899..6e8275f64eea 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 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest 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 f435b227542f..aeebb010eb1e 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 @@ public class AuthContainerView extends LinearLayout 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 98a3e4b55256..11ef749573b7 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.settingslib.Utils; 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 @@ final class WirelessChargingLayout extends FrameLayout { 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 5230159aaef4..0aeab10101f6 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.CLIPBO 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 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv } else if (!mIsMinimized) { setExpandedView(); } - if (mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR) && mClipboardModel.isRemote()) { + if (mClipboardModel.isRemote()) { mTimeoutHandler.cancelTimeout(); mOnUiUpdate = null; } else { @@ -291,8 +290,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv 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 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv 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 044dd6a47241..75f70ae8eb7a 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.dreams.dagger.DreamModule; 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.privacy.PrivacyModule; 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 @@ import javax.inject.Named; PolicyModule.class, PrivacyModule.class, QRCodeScannerModule.class, + QSFragmentStartableModule.class, ScreenshotModule.class, SensorModule.class, SecurityRepositoryModule.class, @@ -198,8 +199,7 @@ import javax.inject.Named; 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 8783ec328b40..aaf4358494f0 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 @@ object Flags { @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 @@ object Flags { 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") @@ -577,16 +577,6 @@ object Flags { // TODO(b/254512507): Tracking Bug val CHOOSER_UNBUNDLED = releasedFlag(1500, "chooser_unbundled") - // TODO(b/266982749) Tracking Bug - val SHARESHEET_RESELECTION_ACTION = releasedFlag(1502, "sharesheet_reselection_action") - - // TODO(b/266983474) Tracking Bug - val SHARESHEET_IMAGE_AND_TEXT_PREVIEW = releasedFlag(1503, "sharesheet_image_text_preview") - - // TODO(b/267355521) Tracking Bug - val SHARESHEET_SCROLLABLE_IMAGE_PREVIEW = - releasedFlag(1504, "sharesheet_scrollable_image_preview") - // 1700 - clipboard @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior") @@ -626,7 +616,8 @@ object Flags { @JvmField val ENABLE_USI_BATTERY_NOTIFICATIONS = releasedFlag(2302, "enable_usi_battery_notifications") - @JvmField val ENABLE_STYLUS_EDUCATION = unreleasedFlag(2303, "enable_stylus_education") + @JvmField val ENABLE_STYLUS_EDUCATION = + unreleasedFlag(2303, "enable_stylus_education", teamfood = true) // 2400 - performance tools and debugging info // TODO(b/238923086): Tracking Bug diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java index 6a27ee7c0c89..81a520661cfe 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java @@ -39,15 +39,16 @@ import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.plugins.Plugin; import com.android.systemui.util.leak.LeakDetector; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; + 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 javax.inject.Provider; public class FragmentHostManager { @@ -322,25 +323,17 @@ public class FragmentHostManager { 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 d302b13a68b6..a75c056c5c71 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 android.view.View; 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 @@ public class FragmentService implements Dumpable { * 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 @@ public class FragmentService implements Dumpable { @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 @@ public class FragmentService implements Dumpable { } } - /** - * 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 @@ public class FragmentService implements Dumpable { 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/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index e0af5ceedb2f..7a2013e2c612 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1957,11 +1957,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, if (mShowing && mKeyguardStateController.isShowing()) { if (mPM.isInteractive() && !mHiding) { // It's already showing, and we're not trying to show it while the screen is off. - // We can simply reset all of the views. + // We can simply reset all of the views, but don't hide the bouncer in case the user + // is currently interacting with it. if (DEBUG) Log.d(TAG, "doKeyguard: not showing (instead, resetting) because it is " + "already showing, we're interactive, and we were not previously hiding. " + "It should be safe to short-circuit here."); - resetStateLocked(); + resetStateLocked(/* hideBouncer= */ false); return; } else { // We are trying to show the keyguard while the screen is off or while we were in @@ -2035,8 +2036,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * @see #handleReset */ private void resetStateLocked() { + resetStateLocked(/* hideBouncer= */ true); + } + + private void resetStateLocked(boolean hideBouncer) { if (DEBUG) Log.e(TAG, "resetStateLocked"); - Message msg = mHandler.obtainMessage(RESET); + Message msg = mHandler.obtainMessage(RESET, hideBouncer ? 1 : 0, 0); mHandler.sendMessage(msg); } @@ -2226,7 +2231,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, handleHide(); break; case RESET: - handleReset(); + handleReset(msg.arg1 != 0); break; case VERIFY_UNLOCK: Trace.beginSection("KeyguardViewMediator#handleMessage VERIFY_UNLOCK"); @@ -3008,10 +3013,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * Handle message sent by {@link #resetStateLocked} * @see #RESET */ - private void handleReset() { + private void handleReset(boolean hideBouncer) { synchronized (KeyguardViewMediator.this) { if (DEBUG) Log.d(TAG, "handleReset"); - mKeyguardViewControllerLazy.get().reset(true /* hideBouncerWhenShowing */); + mKeyguardViewControllerLazy.get().reset(hideBouncer); } scheduleNonStrongBiometricIdleTimeout(); 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 5f6098b8758f..5f2178df4346 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 @@ constructor( 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 @@ constructor( } 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 @@ constructor( "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 @@ constructor( logAndObserve( combine( keyguardInteractor.isSecureCameraActive, - alternateBouncerInteractor.isVisible, + alternateBouncerInteractor.isVisible ) { a, b -> !a || b }, @@ -330,12 +341,12 @@ constructor( logAndObserve(isLockedOut.isFalse(), "isNotInLockOutState", faceAuthLog), logAndObserve( deviceEntryFingerprintAuthRepository.isLockedOut.isFalse(), - "fpLockedOut", + "fpIsNotLockedOut", faceAuthLog ), logAndObserve( trustRepository.isCurrentUserTrusted.isFalse(), - "currentUserTrusted", + "currentUserIsNotTrusted", faceAuthLog ), logAndObserve( @@ -343,11 +354,6 @@ constructor( "nonStrongBiometricIsAllowed", faceAuthLog ), - logAndObserve( - userRepository.selectedUserInfo.map { it.isPrimary }, - "userIsPrimaryUser", - faceAuthLog - ), ) .reduce(::and) .distinctUntilChanged() @@ -385,14 +391,11 @@ constructor( _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 @@ constructor( } } + 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 @@ constructor( * 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 06ae11fe810c..74ef7a50fd44 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 @@ interface KeyguardFaceAuthInteractor { 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 cad40aac00d3..5005b6c7f0df 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 @@ class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInt 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 20ebb711c42d..6b515dab79f6 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 @@ constructor( 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 eded9c1454f2..c8bd958615cf 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 @@ data class ErrorAuthenticationStatus(val msgId: Int, val msg: String?) : Authent * 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 5770f3ee8876..ddce516a0fb2 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 @@ constructor( 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 7f6e4a903d76..efd3ad620de0 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.FaceManager 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 @@ constructor( { "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/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 7edb37832c4f..077ee027d764 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -136,6 +136,14 @@ public class LogModule { return factory.create("NotifRemoteInputLog", 50 /* maxSize */, false /* systrace */); } + /** Provides a logging buffer for all logs related to unseen notifications. */ + @Provides + @SysUISingleton + @UnseenNotificationLog + public static LogBuffer provideUnseenNotificationLogBuffer(LogBufferFactory factory) { + return factory.create("UnseenNotifLog", 20 /* maxSize */, false /* systrace */); + } + /** Provides a logging buffer for all logs related to the data layer of notifications. */ @Provides @SysUISingleton diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/PressHomeBeforeTestRule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/UnseenNotificationLog.java index 6586f630fae4..5c2321be4388 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/PressHomeBeforeTestRule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/UnseenNotificationLog.java @@ -13,22 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.inputmethod.stresstest; -import android.support.test.uiautomator.UiDevice; +package com.android.systemui.log.dagger; -import androidx.test.platform.app.InstrumentationRegistry; +import static java.lang.annotation.RetentionPolicy.RUNTIME; -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; +import com.android.systemui.plugins.log.LogBuffer; -/** This rule will press home before a test case. */ -public class PressHomeBeforeTestRule extends TestWatcher { - private final UiDevice mUiDevice = - UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; - @Override - protected void starting(Description description) { - mUiDevice.pressHome(); - } +import javax.inject.Qualifier; + +/** A {@link LogBuffer} for unseen notification related messages. */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface UnseenNotificationLog { } 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 1c8bfd1fc468..8b7426336b80 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 @@ class MediaViewHolder constructor(itemView: View) { val expandedBottomActionIds = setOf( + R.id.media_progress_bar, R.id.actionPrev, R.id.actionNext, R.id.action0, @@ -155,7 +156,22 @@ class MediaViewHolder constructor(itemView: View) { 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 2509f21242cd..35f5a8ca4345 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.MediaMetadata 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.plugins.FalsingManager 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 @@ constructor( @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 @@ constructor( 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 @@ constructor( } } + /** + * 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 @@ constructor( } 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 @@ constructor( return false } detector.onTouchEvent(event) + // Store the last motion event done on seekbar. + viewModel.lastMotionEvent = event.copy() return !shouldGoToSeekBar } @@ -459,6 +493,8 @@ constructor( 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 70f2dee006ed..0b33904829d5 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 @@ class RecommendationViewHolder private constructor(itemView: View, updatedView: 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 cd51d92062bc..4bca778b77c5 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 @@ constructor( 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 @@ constructor( 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 @@ constructor( 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 @@ constructor( 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 8f703765fbd9..aab898e2efcf 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 @@ constructor( 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 @@ constructor( 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 2b9f0af046ff..a75834760d30 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 616f9b526156..89a8526ff42f 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.content.pm.PackageManager.ApplicationInfoFlags 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 @@ class NoteTaskInfoResolver 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 @@ constructor( 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 000000000000..253560b93f1a --- /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/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 9e204e48da8e..34ecce9fa6fd 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -2809,6 +2809,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Override public void setBouncerShowing(boolean bouncerShowing) { mBouncerShowing = bouncerShowing; + mNotificationStackScrollLayoutController.updateShowEmptyShadeView(); updateVisibility(); } @@ -3415,8 +3416,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump Log.d(TAG, "Updating panel sysui state flags: fullyExpanded=" + isFullyExpanded() + " inQs=" + mQsController.getExpanded()); } + boolean isPanelVisible = mCentralSurfaces != null && mCentralSurfaces.isPanelExpanded(); mSysUiState - .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE, getExpandedFraction() > 0) + .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE, isPanelVisible) .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED, isFullyExpanded() && !mQsController.getExpanded()) .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt index 4cbbefe1cd73..2fa070ca20b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt @@ -21,8 +21,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.os.UserHandle import android.provider.Settings import androidx.annotation.VisibleForTesting +import com.android.systemui.Dumpable import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState @@ -42,8 +44,14 @@ import com.android.systemui.statusbar.notification.collection.provider.SeenNotif import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.headsUpEvents +import com.android.systemui.util.asIndenting +import com.android.systemui.util.indentIfPossible import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SettingsProxyExt.observerFlow +import java.io.PrintWriter +import javax.inject.Inject +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -57,13 +65,11 @@ import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch import kotlinx.coroutines.yield -import javax.inject.Inject -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds /** * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section @@ -74,17 +80,19 @@ class KeyguardCoordinator @Inject constructor( @Background private val bgDispatcher: CoroutineDispatcher, + private val dumpManager: DumpManager, private val headsUpManager: HeadsUpManager, private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider, private val keyguardRepository: KeyguardRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository, + private val logger: KeyguardCoordinatorLogger, private val notifPipelineFlags: NotifPipelineFlags, @Application private val scope: CoroutineScope, private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider, private val secureSettings: SecureSettings, private val seenNotifsProvider: SeenNotificationsProviderImpl, private val statusBarStateController: StatusBarStateController, -) : Coordinator { +) : Coordinator, Dumpable { private val unseenNotifications = mutableSetOf<NotificationEntry>() private var unseenFilterEnabled = false @@ -103,6 +111,7 @@ constructor( pipeline.addCollectionListener(collectionListener) scope.launch { trackUnseenNotificationsWhileUnlocked() } scope.launch { invalidateWhenUnseenSettingChanges() } + dumpManager.registerDumpable(this) } private suspend fun trackUnseenNotificationsWhileUnlocked() { @@ -122,14 +131,16 @@ constructor( // If the screen is turning off, stop tracking, but if that transition is // cancelled, then start again. emitAll( - keyguardTransitionRepository.transitions - .map { step -> !step.isScreenTurningOff } + keyguardTransitionRepository.transitions.map { step -> + !step.isScreenTurningOff + } ) } } // Prevent double emit of `false` caused by transition to AOD, followed by keyguard // showing .distinctUntilChanged() + .onEach { trackingUnseen -> logger.logTrackingUnseen(trackingUnseen) } // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is // showing again @@ -140,9 +151,11 @@ constructor( // set when unlocked awaitTimeSpentNotDozing(SEEN_TIMEOUT) clearUnseenOnBeginTracking = true + logger.logSeenOnLockscreen() } else { if (clearUnseenOnBeginTracking) { clearUnseenOnBeginTracking = false + logger.logAllMarkedSeenOnUnlock() unseenNotifications.clear() } unseenNotifFilter.invalidateList("keyguard no longer showing") @@ -166,6 +179,8 @@ constructor( .first() } + // Track "unseen" notifications, marking them as seen when either shade is expanded or the + // notification becomes heads up. private suspend fun trackUnseenNotifications() { coroutineScope { launch { clearUnseenNotificationsWhenShadeIsExpanded() } @@ -179,6 +194,7 @@ constructor( // keyguard transition and not the user expanding the shade yield() if (isExpanded) { + logger.logShadeExpanded() unseenNotifications.clear() } } @@ -190,6 +206,7 @@ constructor( .forEach { unseenNotifications.remove(it) } headsUpManager.headsUpEvents.collect { (entry, isHun) -> if (isHun) { + logger.logUnseenHun(entry.key) unseenNotifications.remove(entry) } } @@ -231,6 +248,7 @@ constructor( if ( keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded ) { + logger.logUnseenAdded(entry.key) unseenNotifications.add(entry) } } @@ -239,12 +257,15 @@ constructor( if ( keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded ) { + logger.logUnseenUpdated(entry.key) unseenNotifications.add(entry) } } override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { - unseenNotifications.remove(entry) + if (unseenNotifications.remove(entry)) { + logger.logUnseenRemoved(entry.key) + } } } @@ -272,6 +293,7 @@ constructor( }.also { hasFiltered -> hasFilteredAnyNotifs = hasFilteredAnyNotifs || hasFiltered } override fun onCleanup() { + logger.logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs) seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs hasFilteredAnyNotifs = false } @@ -306,11 +328,25 @@ constructor( sectionHeaderVisibilityProvider.sectionHeadersVisible = showSections } + override fun dump(pw: PrintWriter, args: Array<out String>) = + with(pw.asIndenting()) { + println( + "seenNotifsProvider.hasFilteredOutSeenNotifications=" + + seenNotifsProvider.hasFilteredOutSeenNotifications + ) + println("unseen notifications:") + indentIfPossible { + for (notification in unseenNotifications) { + println(notification.key) + } + } + } + companion object { private const val TAG = "KeyguardCoordinator" private val SEEN_TIMEOUT = 5.seconds } } -private val TransitionStep.isScreenTurningOff: Boolean get() = - transitionState == TransitionState.STARTED && to != KeyguardState.GONE
\ No newline at end of file +private val TransitionStep.isScreenTurningOff: Boolean + get() = transitionState == TransitionState.STARTED && to != KeyguardState.GONE diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt new file mode 100644 index 000000000000..6503a6403eaa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator + +import com.android.systemui.log.dagger.UnseenNotificationLog +import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.plugins.log.LogLevel +import javax.inject.Inject + +private const val TAG = "KeyguardCoordinator" + +class KeyguardCoordinatorLogger +@Inject +constructor( + @UnseenNotificationLog private val buffer: LogBuffer, +) { + fun logSeenOnLockscreen() = + buffer.log( + TAG, + LogLevel.DEBUG, + "Notifications on lockscreen will be marked as seen when unlocked." + ) + + fun logTrackingUnseen(trackingUnseen: Boolean) = + buffer.log( + TAG, + LogLevel.DEBUG, + messageInitializer = { bool1 = trackingUnseen }, + messagePrinter = { "${if (bool1) "Start" else "Stop"} tracking unseen notifications." }, + ) + + fun logAllMarkedSeenOnUnlock() = + buffer.log( + TAG, + LogLevel.DEBUG, + "Notifications have been marked as seen now that device is unlocked." + ) + + fun logShadeExpanded() = + buffer.log( + TAG, + LogLevel.DEBUG, + "Notifications have been marked as seen due to shade expansion." + ) + + fun logUnseenAdded(key: String) = + buffer.log( + TAG, + LogLevel.DEBUG, + messageInitializer = { str1 = key }, + messagePrinter = { "Unseen notif added: $str1" }, + ) + + fun logUnseenUpdated(key: String) = + buffer.log( + TAG, + LogLevel.DEBUG, + messageInitializer = { str1 = key }, + messagePrinter = { "Unseen notif updated: $str1" }, + ) + + fun logUnseenRemoved(key: String) = + buffer.log( + TAG, + LogLevel.DEBUG, + messageInitializer = { str1 = key }, + messagePrinter = { "Unseen notif removed: $str1" }, + ) + + fun logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs: Boolean) = + buffer.log( + TAG, + LogLevel.DEBUG, + messageInitializer = { bool1 = hasFilteredAnyNotifs }, + messagePrinter = { "UI showing unseen filter treatment: $bool1" }, + ) + + fun logUnseenHun(key: String) = + buffer.log( + TAG, + LogLevel.DEBUG, + messageInitializer = { str1 = key }, + messagePrinter = { "Unseen notif has become heads up: $str1" }, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index e2f93ce62e46..4177263efbd0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -1235,7 +1235,8 @@ public class NotificationStackScrollLayoutController { // Hide empty shade view when in transition to Keyguard. // That avoids "No Notifications" to blink when transitioning to AOD. // For more details, see: b/228790482 - && !isInTransitionToKeyguard(); + && !isInTransitionToKeyguard() + && !mCentralSurfaces.isBouncerShowing(); mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade()); 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 fab334c5ebc4..db20eeccf763 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.NotificationStackScroll 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; @@ -1510,14 +1511,15 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { boolean tracking = event.getTracking(); dispatchPanelExpansionForKeyguardDismiss(fraction, tracking); - if (getShadeViewController() != null) { - getShadeViewController().updateSystemUiStateFlags(); - } - if (fraction == 0 || fraction == 1) { if (getNavigationBarView() != null) { getNavigationBarView().onStatusBarPanelStateChanged(); } + if (getShadeViewController() != null) { + // Needed to update SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED and + // SYSUI_STATE_QUICK_SETTINGS_EXPANDED + getShadeViewController().updateSystemUiStateFlags(); + } } } @@ -1525,6 +1527,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { void onShadeExpansionFullyChanged(Boolean isExpanded) { if (mPanelExpanded != isExpanded) { mPanelExpanded = isExpanded; + if (getShadeViewController() != null) { + // Needed to update SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE + getShadeViewController().updateSystemUiStateFlags(); + } if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) { if (DEBUG) { Log.v(TAG, "clearing notification effects from Height"); @@ -1632,7 +1638,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } } mCentralSurfacesComponent = mCentralSurfacesComponentFactory.create(); - mFragmentService.addFragmentInstantiationProvider(mCentralSurfacesComponent); + mFragmentService.addFragmentInstantiationProvider( + CollapsedStatusBarFragment.class, + mCentralSurfacesComponent::createCollapsedStatusBarFragment); mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView(); mNotificationShadeWindowViewController = mCentralSurfacesComponent diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt index 39dda8c3d191..6026d2cb6858 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt +++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt @@ -50,6 +50,7 @@ class DynamicColors { Pair.create("surface_variant", MDC.surfaceVariant), Pair.create("on_surface_variant", MDC.onSurfaceVariant), Pair.create("outline", MDC.outline), + Pair.create("outline_variant", MDC.outlineVariant), Pair.create("error", MDC.error), Pair.create("on_error", MDC.onError), Pair.create("error_container", MDC.errorContainer), diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index a9920ec77c23..8f4b32006919 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 @@ class ClockEventControllerTest : SysuiTestCase() { 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 @@ class ClockEventControllerTest : SysuiTestCase() { keyguardCaptor.value.onKeyguardVisibilityChanged(true) batteryCaptor.value.onBatteryLevelChanged(10, false, true) - verify(animations).charge() + verify(animations, times(2)).charge() } @Test @@ -198,7 +199,7 @@ class ClockEventControllerTest : SysuiTestCase() { 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 @@ class ClockEventControllerTest : SysuiTestCase() { 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 @@ class ClockEventControllerTest : SysuiTestCase() { 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 fc906dedfa1d..95db0c096faf 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 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { 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 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { 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 2c1d2adb11ee..a2c632936047 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 class KeyguardStatusViewControllerTest extends SysuiTestCase { 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 class KeyguardStatusViewControllerTest extends SysuiTestCase { 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 3eb9590c0b95..2962c14b813a 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.Mock; 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 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { 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 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mMockitoSession = ExtendedMockito.mockitoSession() .spyStatic(SubscriptionManager.class) + .strictness(Strictness.WARN) .startMocking(); ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID) .when(SubscriptionManager::getDefaultSubscriptionId); @@ -958,7 +960,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { 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 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // Preconditions for sfps auth to run keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1501,7 +1503,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // GIVEN Preconditions for sfps auth to run keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1530,7 +1532,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // GIVEN Preconditions for sfps auth to run keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1682,7 +1684,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // Face auth should run when the following is true. keyguardNotGoingAway(); occludingAppRequestsFaceAuth(); - currentUserIsPrimary(); + currentUserIsSystem(); primaryAuthNotRequiredByStrongAuthTracker(); biometricsEnabledForCurrentUser(); currentUserDoesNotHaveTrust(); @@ -1703,7 +1705,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // Face auth should run when the following is true. bouncerFullyVisibleAndNotGoingToSleep(); keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); primaryAuthNotRequiredByStrongAuthTracker(); biometricsEnabledForCurrentUser(); currentUserDoesNotHaveTrust(); @@ -1726,7 +1728,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // Face auth should run when the following is true. bouncerFullyVisibleAndNotGoingToSleep(); keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); primaryAuthNotRequiredByStrongAuthTracker(); biometricsEnabledForCurrentUser(); currentUserDoesNotHaveTrust(); @@ -1747,7 +1749,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { 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 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // Face auth should run when the following is true. keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsPrimary(); + currentUserIsSystem(); biometricsEnabledForCurrentUser(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); @@ -1789,7 +1791,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { throws RemoteException { keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1811,7 +1813,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // Face auth should run when the following is true. keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1831,7 +1833,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { throws RemoteException { keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1852,7 +1854,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // Face auth should run when the following is true. keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1874,7 +1876,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { throws RemoteException { // Face auth should run when the following is true. keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1894,7 +1896,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { throws RemoteException { // Face auth should run when the following is true. keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1913,7 +1915,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testShouldListenForFace_whenKeyguardIsAwake_returnsTrue() throws RemoteException { // Preconditions for face auth to run keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1938,7 +1940,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testShouldListenForFace_whenUdfpsFingerDown_returnsTrue() throws RemoteException { // Preconditions for face auth to run keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1957,7 +1959,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { throws RemoteException { // Preconditions for face auth to run keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -1975,7 +1977,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { throws RemoteException { // Preconditions for face auth to run keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -2000,7 +2002,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { throws RemoteException { // Preconditions for face auth to run keyguardNotGoingAway(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -2322,7 +2324,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { throws RemoteException { keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -2453,7 +2455,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_CLOSED; keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -2477,7 +2479,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_UNKNOWN; keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsPrimary(); + currentUserIsSystem(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); @@ -2528,19 +2530,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @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 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { 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 80c3e5eaff15..937a7a92ec46 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 @@ class CameraGestureHelperTest : SysuiTestCase() { } @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 @@ class CameraGestureHelperTest : SysuiTestCase() { } @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 @@ class CameraGestureHelperTest : SysuiTestCase() { } @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 @@ class CameraGestureHelperTest : SysuiTestCase() { } @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 8600b7c48d33..fe5fa1fdd39f 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.CLIPBO 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 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { 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 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @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 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { } @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 class ClipboardOverlayControllerTest extends SysuiTestCase { 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 fe352fd078f0..1b2fc93ddb3f 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 @@ class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() { } @Test - fun `long-press`() = runTest { + fun longPress() = runTest { val downX = 123 val downY = 456 dispatchTouchEvents( @@ -91,7 +91,7 @@ class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() { } @Test - fun `long-press but feature not enabled`() = runTest { + fun longPressButFeatureNotEnabled() = runTest { underTest.isLongPressHandlingEnabled = false dispatchTouchEvents( Down( @@ -106,7 +106,7 @@ class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() { } @Test - fun `long-press but view not attached`() = runTest { + fun longPressButViewNotAttached() = runTest { isAttachedToWindow = false dispatchTouchEvents( Down( @@ -121,7 +121,7 @@ class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() { } @Test - fun `dragged too far to be considered a long-press`() = runTest { + fun draggedTooFarToBeConsideredAlongPress() = runTest { dispatchTouchEvents( Down( x = 123, @@ -138,7 +138,7 @@ class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() { } @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 87c66b5a98ac..75eec72a949c 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 @@ class DemoModeControllerTest : SysuiTestCase() { } @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 a2dc1eb606ed..4ba1bc6dfbbb 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.os.Looper 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 @@ import org.junit.Test @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 @@ class FragmentServiceTest : SysuiTestCase() { 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()) - - assertThat(fragmentService.injectionMap).hasSize(2) - } - - @Test - fun addFragmentInstantiationProvider_objectHasFragmentMethods_methodsAdded() { - fragmentService.addFragmentInstantiationProvider( - @Suppress("unused") - object : Any() { - fun createTestFragment2() = TestFragment2() - fun createTestFragment3() = TestFragment3() - } - ) + fragmentService.addFragmentInstantiationProvider(TestFragment::class.java) { + TestFragment() + } - 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() - } - ) - - assertThat(fragmentService.injectionMap).hasSize(2) - } + fragmentService.addFragmentInstantiationProvider(TestFragment::class.java) { + TestFragment() + } + fragmentService.addFragmentInstantiationProvider(TestFragment::class.java) { + TestFragment() + } - class TestFragmentCreator : FragmentService.FragmentCreator { - override fun createQSFragment(): QSFragment = mock() - @Suppress("unused") - fun createTestFragment(): TestFragmentInCreator = TestFragmentInCreator() + assertThat(fragmentService.injectionMap).hasSize(1) } - 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 1044131d17c0..4daecd91efa0 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 @@ class CustomizationProviderTest : SysuiTestCase() { } @Test - fun `onAttachInfo - reportsContext`() { + fun onAttachInfo_reportsContext() { val callback: SystemUIAppComponentFactoryBase.ContextAvailableCallback = mock() underTest.setContextAvailableCallback(callback) @@ -254,7 +254,7 @@ class CustomizationProviderTest : SysuiTestCase() { } @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 @@ class CustomizationProviderTest : SysuiTestCase() { } @Test - fun `query slots`() = + fun querySlotsProvidesTwoSlots() = testScope.runTest { assertThat(querySlots()) .isEqualTo( @@ -296,7 +296,7 @@ class CustomizationProviderTest : SysuiTestCase() { } @Test - fun `query affordances`() = + fun queryAffordancesProvidesTwoAffordances() = testScope.runTest { assertThat(queryAffordances()) .isEqualTo( @@ -316,7 +316,7 @@ class CustomizationProviderTest : SysuiTestCase() { } @Test - fun `delete and query selection`() = + fun deleteAndQuerySelection() = testScope.runTest { insertSelection( slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, @@ -351,7 +351,7 @@ class CustomizationProviderTest : SysuiTestCase() { } @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/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 0de9608b906f..8f58140bce43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -502,7 +502,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); assertTrue(mViewMediator.isShowingAndNotOccluded()); - verify(mStatusBarKeyguardViewManager).reset(anyBoolean()); + verify(mStatusBarKeyguardViewManager).reset(false); } @Test 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 5bb8367432fc..e20d3afaca53 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 @@ class CameraQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `affordance triggered -- camera launch called`() { + fun affordanceTriggered_cameraLaunchCalled() { // When val result = underTest.onTriggered(null) @@ -84,7 +84,7 @@ class CameraQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `getPickerScreenState - default when launchable`() = + fun getPickerScreenState_defaultWhenLaunchable() = testScope.runTest { setLaunchable(true) @@ -93,7 +93,7 @@ class CameraQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `getPickerScreenState - unavailable when camera app not installed`() = + fun getPickerScreenState_unavailableWhenCameraAppNotInstalled() = testScope.runTest { setLaunchable(isCameraAppInstalled = false) @@ -102,7 +102,7 @@ class CameraQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `getPickerScreenState - unavailable when camera disabled by admin`() = + fun getPickerScreenState_unavailableWhenCameraDisabledByAdmin() = testScope.runTest { setLaunchable(isCameraDisabledByDeviceAdmin = true) @@ -111,7 +111,7 @@ class CameraQuickAffordanceConfigTest : SysuiTestCase() { } @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 64839e2c1105..c326a86a4011 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 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `dnd not available - picker state hidden`() = + fun dndNotAvailable_pickerStateHidden() = testScope.runTest { // given whenever(zenModeController.isZenAvailable).thenReturn(false) @@ -113,7 +113,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `dnd available - picker state visible`() = + fun dndAvailable_pickerStateVisible() = testScope.runTest { // given whenever(zenModeController.isZenAvailable).thenReturn(true) @@ -132,7 +132,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() { } @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 31391ee8c0eb..292d06780c5f 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 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { } @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 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { } @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 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { } @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 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { } @Test - fun `flashlight availability now off -- hidden`() = runTest { + fun flashlightAvailabilityNowOff_hidden() = runTest { // given flashlightController.isEnabled = true flashlightController.isAvailable = false @@ -146,7 +146,7 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { } @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 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { } @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 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { } @Test - fun `flashlight available -- picker state default`() = runTest { + fun flashlightAvailable_pickerStateDefault() = runTest { // given flashlightController.isAvailable = true @@ -202,7 +202,7 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { } @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 2c1c04cefdc7..26f0cdbc2caa 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 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 3bae7f709b69..9a18ba847de8 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 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() { } @Test - fun `Setting a setting selects the affordance`() = + fun settingAsettingSelectsTheAffordance() = testScope.runTest { val job = underTest.startSyncing() @@ -129,7 +129,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() { } @Test - fun `Clearing a setting selects the affordance`() = + fun clearingAsettingSelectsTheAffordance() = testScope.runTest { val job = underTest.startSyncing() @@ -156,7 +156,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() { } @Test - fun `Selecting an affordance sets its setting`() = + fun selectingAnAffordanceSetsItsSetting() = testScope.runTest { val job = underTest.startSyncing() @@ -172,7 +172,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() { } @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 1259b478dec5..6989f4467080 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 @@ class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() { } @Test - fun `remembers selections by user`() = runTest { + fun remembersSelectionsByUser() = runTest { overrideResource( R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>(), @@ -246,7 +246,7 @@ class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() { } @Test - fun `selections respects defaults`() = runTest { + fun selectionsRespectsDefaults() = runTest { val slotId1 = "slot1" val slotId2 = "slot2" val affordanceId1 = "affordance1" @@ -277,7 +277,7 @@ class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() { } @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 @@ class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() { } @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 @@ class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() { } @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 c08ef42eef03..a1c9f87ee7bc 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 @@ class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() { } @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 @@ class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() { } @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 925c06fe8951..c38827a73e5f 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 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() { } @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 facc7475f034..f243d7b45990 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 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { } @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 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { } @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 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { } @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 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { } @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 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { } @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 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { } @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 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { } @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 1adf808cf645..faf18d379270 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 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 752963fad92d..952882da1db5 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 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `getPickerScreenState - default`() = runTest { + fun getPickerScreenState_default() = runTest { setUpState() assertThat(underTest.getPickerScreenState()) @@ -151,7 +151,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `getPickerScreenState - unavailable`() = runTest { + fun getPickerScreenState_unavailable() = runTest { setUpState( isWalletServiceAvailable = false, ) @@ -161,7 +161,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `getPickerScreenState - disabled when the feature is not enabled`() = runTest { + fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = runTest { setUpState( isWalletEnabled = false, ) @@ -171,7 +171,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @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 f1b9c5f0fff8..a9b9c9011636 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 @@ class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `lockScreenState - visible when launchable`() = + fun lockScreenState_visibleWhenLaunchable() = testScope.runTest { setLaunchable() @@ -84,7 +84,7 @@ class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `lockScreenState - hidden when app not installed on device`() = + fun lockScreenState_hiddenWhenAppNotInstalledOnDevice() = testScope.runTest { setLaunchable(isVideoCameraAppInstalled = false) @@ -95,7 +95,7 @@ class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `lockScreenState - hidden when camera disabled by admin`() = + fun lockScreenState_hiddenWhenCameraDisabledByAdmin() = testScope.runTest { setLaunchable(isCameraDisabledByAdmin = true) @@ -106,7 +106,7 @@ class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `getPickerScreenState - default when launchable`() = + fun getPickerScreenState_defaultWhenLaunchable() = testScope.runTest { setLaunchable() @@ -115,7 +115,7 @@ class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun `getPickerScreenState - unavailable when app not installed on device`() = + fun getPickerScreenState_unavailableWhenAppNotInstalledOnDevice() = testScope.runTest { setLaunchable(isVideoCameraAppInstalled = false) @@ -124,7 +124,7 @@ class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() { } @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 fc75d47c01b7..fa40fc431b5f 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.app.StatusBarManager.SESSION_KEYGUARD 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 com.google.common.truth.Truth.assertThat 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 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun authenticateDoesNotRunWhenCurrentUserIsNotPrimary() = - testScope.runTest { - testGatingCheckForFaceAuth { - launch { fakeUserRepository.setSelectedUserInfo(secondaryUser) } - } - } - - @Test fun authenticateDoesNotRunWhenSecureCameraIsActive() = testScope.runTest { testGatingCheckForFaceAuth { @@ -652,6 +644,58 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @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 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { 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 a668af340e7b..12b8261fd140 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 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() { } @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 3fd97da23157..b53a43427ec7 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 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { } @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 d9d4013b09b9..d0bfaa9bedc9 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 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { } @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 f9493d10ff61..9daf3f3ae2f0 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 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() { } @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 3d1d2f46a65e..5da1a846fbfd 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 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { } @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 77bb12c2cbda..8a0cf4f84da9 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 @@ class KeyguardLongPressInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardLongPressInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardLongPressInteractorTest : SysuiTestCase() { } @Test - fun `logs when menu is shown`() = + fun logsWhenMenuIsShown() = testScope.runTest { collectLastValue(underTest.isMenuVisible) runCurrent() @@ -223,7 +223,7 @@ class KeyguardLongPressInteractorTest : SysuiTestCase() { } @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 503e0025ce5c..96fff6499be5 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 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test - fun `quickAffordance - bottom start affordance is visible`() = + fun quickAffordance_bottomStartAffordanceIsVisible() = testScope.runTest { val configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS homeControls.setState( @@ -221,7 +221,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test - fun `quickAffordance - hidden when quick settings is visible`() = + fun quickAffordance_hiddenWhenQuickSettingsIsVisible() = testScope.runTest { repository.setQuickSettingsVisible(true) quickAccessWallet.setState( @@ -302,7 +302,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test - fun `quickAffordance - bottom start affordance hidden while dozing`() = + fun quickAffordance_bottomStartAffordanceHiddenWhileDozing() = testScope.runTest { repository.setDozing(true) homeControls.setState( @@ -319,7 +319,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test - fun `unselect - one`() = + fun unselect_one() = testScope.runTest { featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true) homeControls.setState( @@ -588,7 +588,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @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 276b3e39180b..503687df25d9 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 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { } @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 e2d0ec3ae01b..fe65236c699f 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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test - fun `DREAMING to LOCKSCREEN`() = + fun DREAMINGtoLOCKSCREEN() = testScope.runTest { // GIVEN a device is dreaming keyguardRepository.setDreamingWithOverlay(true) @@ -215,7 +215,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test - fun `OCCLUDED to DOZING`() = + fun OCCLUDEDtoDOZING() = testScope.runTest { // GIVEN a device with AOD not available keyguardRepository.setAodAvailable(false) @@ -269,7 +269,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test - fun `OCCLUDED to AOD`() = + fun OCCLUDEDtoAOD() = testScope.runTest { // GIVEN a device with AOD available keyguardRepository.setAodAvailable(true) @@ -296,7 +296,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test - fun `LOCKSCREEN to DOZING`() = + fun LOCKSCREENtoDOZING() = testScope.runTest { // GIVEN a device with AOD not available keyguardRepository.setAodAvailable(false) @@ -354,7 +354,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test - fun `LOCKSCREEN to AOD`() = + fun LOCKSCREENtoAOD() = testScope.runTest { // GIVEN a device with AOD available keyguardRepository.setAodAvailable(true) @@ -381,7 +381,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test - fun `GONE to DOZING`() = + fun GONEtoDOZING() = testScope.runTest { // GIVEN a device with AOD not available keyguardRepository.setAodAvailable(false) @@ -480,7 +480,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test - fun `GONE to AOD`() = + fun GONEtoAOD() = testScope.runTest { // GIVEN a device with AOD available keyguardRepository.setAodAvailable(true) @@ -507,7 +507,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test - fun `OCCLUDED to GONE`() = + fun OCCLUDEDtoGONE() = testScope.runTest { // GIVEN a device on lockscreen keyguardRepository.setKeyguardShowing(true) @@ -785,7 +785,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @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 62366164a17d..359854b34496 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 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { } @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 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { } @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 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { } @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 224eec1bf3ac..2361c59a5c64 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 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @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 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @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 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @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 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @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 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @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 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @Test - fun `startButton - not present - model is hidden`() = + fun startButton_notPresent_modelIsHidden() = testScope.runTest { val latest = collectLastValue(underTest.startButton) @@ -524,7 +524,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @Test - fun `alpha - in preview mode - does not change`() = + fun alpha_inPreviewMode_doesNotChange() = testScope.runTest { underTest.enablePreviewMode( initiallySelectedSlotId = null, @@ -629,7 +629,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @Test - fun `isClickable - true when alpha at threshold`() = + fun isClickable_trueWhenAlphaAtThreshold() = testScope.runTest { repository.setKeyguardShowing(true) repository.setBottomAreaAlpha( @@ -661,7 +661,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @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 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @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 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @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 0c4e84521a36..efa5f0c966e3 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 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { 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 ea11f01ed580..afab25092278 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 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @Test(expected = IllegalStateException::class) - fun `repeatWhenAttached - enforces main thread`() = + fun repeatWhenAttached_enforcesMainThread() = testScope.runTest { Assert.setTestThread(null) @@ -96,7 +96,7 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @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 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @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 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @Test - fun `repeatWhenAttached - view already attached - immediately runs block`() = + fun repeatWhenAttached_viewAlreadyAttached_immediatelyRunsBlock() = testScope.runTest { whenever(view.isAttachedToWindow).thenReturn(true) @@ -132,7 +132,7 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @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 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @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 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @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 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @Test - fun `repeatWhenAttached - becomes visible without focus - STARTED`() = + fun repeatWhenAttached_becomesVisibleWithoutFocus_STARTED() = testScope.runTest { whenever(view.isAttachedToWindow).thenReturn(true) repeatWhenAttached() @@ -188,7 +188,7 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @Test - fun `repeatWhenAttached - gains focus but invisible - CREATED`() = + fun repeatWhenAttached_gainsFocusButInvisible_CREATED() = testScope.runTest { whenever(view.isAttachedToWindow).thenReturn(true) repeatWhenAttached() @@ -204,7 +204,7 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @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 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @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 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @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 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @Test - fun `repeatWhenAttached - dispose attached`() = + fun repeatWhenAttached_disposeAttached() = testScope.runTest { whenever(view.isAttachedToWindow).thenReturn(true) val handle = repeatWhenAttached() @@ -269,7 +269,7 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @Test - fun `repeatWhenAttached - dispose never attached`() = + fun repeatWhenAttached_disposeNeverAttached() = testScope.runTest { whenever(view.isAttachedToWindow).thenReturn(false) val handle = repeatWhenAttached() @@ -281,7 +281,7 @@ class RepeatWhenAttachedTest : SysuiTestCase() { } @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 411b1bd04c52..af83a560d7d0 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 @@ class TableLogBufferFactoryTest : SysuiTestCase() { 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 @@ class TableLogBufferFactoryTest : SysuiTestCase() { } @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 56c91bc4525d..e3c8b052327c 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.MediaSession 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 @@ public class SeekBarViewModelTest : SysuiTestCase() { 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 + val pos = 40 + 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 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.attachTouchHandlers(mockBar) + viewModel.updateController(mockController) + // WHEN user ends drag + 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 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 4565762929d7..c9956f36dbeb 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 android.view.View 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 @@ class MediaViewControllerTest : SysuiTestCase() { @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 @@ class MediaViewControllerTest : SysuiTestCase() { 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 @@ class MediaViewControllerTest : SysuiTestCase() { 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 @@ class MediaViewControllerTest : SysuiTestCase() { 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 a03bc1e67f3d..7dc622b86b4e 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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { 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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { .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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { .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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { .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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { .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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { 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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { 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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { 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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { 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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { 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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { @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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { 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 @@ internal class NoteTaskControllerTest : SysuiTestCase() { 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 a4df346776a0..b4f5528cb523 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 @@ internal class NoteTaskEventLoggerTest : SysuiTestCase() { 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 0c945dfa4b4c..e09c804e4611 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.content.pm.PackageManager 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 @@ internal class NoteTaskInfoResolverTest : SysuiTestCase() { @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 @@ internal class NoteTaskInfoResolverTest : SysuiTestCase() { ) .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 @@ internal class NoteTaskInfoResolverTest : SysuiTestCase() { ) .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 @@ internal class NoteTaskInfoResolverTest : SysuiTestCase() { 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 91cd6ae5d988..34354504abf0 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 @@ import org.junit.runner.RunWith 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 249a91b0982a..bb3b3f709a7b 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 @@ class PowerRepositoryImplTest : SysuiTestCase() { } @Test - fun `isInteractive - registers for broadcasts`() = + fun isInteractive_registersForBroadcasts() = runBlocking(IMMEDIATE) { val job = underTest.isInteractive.onEach {}.launchIn(this) @@ -78,7 +78,7 @@ class PowerRepositoryImplTest : SysuiTestCase() { } @Test - fun `isInteractive - unregisters from broadcasts`() = + fun isInteractive_unregistersFromBroadcasts() = runBlocking(IMMEDIATE) { val job = underTest.isInteractive.onEach {}.launchIn(this) verifyRegistered() @@ -89,7 +89,7 @@ class PowerRepositoryImplTest : SysuiTestCase() { } @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 @@ class PowerRepositoryImplTest : SysuiTestCase() { } @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 @@ class PowerRepositoryImplTest : SysuiTestCase() { } @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 @@ class PowerRepositoryImplTest : SysuiTestCase() { } @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 @@ class PowerRepositoryImplTest : SysuiTestCase() { } @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 bf6a37ec8eff..31d451227a9b 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 @@ class PowerInteractorTest : SysuiTestCase() { } @Test - fun `isInteractive - screen turns off`() = + fun isInteractive_screenTurnsOff() = runBlocking(IMMEDIATE) { repository.setInteractive(true) var value: Boolean? = null @@ -60,7 +60,7 @@ class PowerInteractorTest : SysuiTestCase() { } @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 8cb5d31fff22..355c4b667333 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 @@ class OverviewProxyServiceTest : SysuiTestCase() { } @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 @@ class OverviewProxyServiceTest : SysuiTestCase() { } @Test - fun `WakefulnessLifecycle - dispatchStartedWakingUp sets SysUI flag to WAKING`() { + fun wakefulnessLifecycle_dispatchStartedWakingUpSetsSysUIflagToWAKING() { wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN) verify(overviewProxy) @@ -177,7 +177,7 @@ class OverviewProxyServiceTest : SysuiTestCase() { } @Test - fun `WakefulnessLifecycle - dispatchFinishedGoingToSleep sets SysUI flag to ASLEEP`() { + fun wakefulnessLifecycle_dispatchFinishedGoingToSleepSetsSysUIflagToASLEEP() { wakefulnessLifecycle.dispatchFinishedGoingToSleep() verify(overviewProxy) @@ -187,7 +187,7 @@ class OverviewProxyServiceTest : SysuiTestCase() { } @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 57b6b2bd6fde..beb981d89735 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 @@ class UserTrackerImplReceiveTest : SysuiTestCase() { } @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 b547318a99b6..d421acac2daa 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 @@ class QsBatteryModeControllerTest : SysuiTestCase() { } @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 @@ class QsBatteryModeControllerTest : SysuiTestCase() { } @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 @@ class QsBatteryModeControllerTest : SysuiTestCase() { } @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 @@ class QsBatteryModeControllerTest : SysuiTestCase() { } @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 76f7401adbb2..9fe75abddf9c 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 @@ class ShadeHeaderControllerTest : SysuiTestCase() { } @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 @@ class ShadeHeaderControllerTest : SysuiTestCase() { } @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 7fa27f34cd9f..3f3faf7f2327 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 @@ class DefaultClockProviderTest : SysuiTestCase() { @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 000000000000..2b4a7fb4803b --- /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/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt index 8f07f8d1a099..c3f51233f59a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt @@ -24,6 +24,7 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.advanceTimeBy +import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState @@ -380,10 +381,12 @@ class KeyguardCoordinatorTest : SysuiTestCase() { val keyguardCoordinator = KeyguardCoordinator( testDispatcher, + mock<DumpManager>(), headsUpManager, keyguardNotifVisibilityProvider, keyguardRepository, keyguardTransitionRepository, + mock<KeyguardCoordinatorLogger>(), notifPipelineFlags, testScope.backgroundScope, sectionHeaderVisibilityProvider, 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 63cb30ca4a33..95b132d603dc 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 @@ class SystemUiCarrierConfigTest : SysuiTestCase() { } @Test - fun `process new config - reflected by isUsingDefault`() { + fun processNewConfig_reflectedByIsUsingDefault() { // Starts out using the defaults assertThat(underTest.isUsingDefault).isTrue() @@ -50,7 +50,7 @@ class SystemUiCarrierConfigTest : SysuiTestCase() { } @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 @@ class SystemUiCarrierConfigTest : SysuiTestCase() { } @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 dfef62e95eda..6e3af26121bf 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 @@ class CarrierConfigRepositoryTest : SysuiTestCase() { } @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 @@ class CarrierConfigRepositoryTest : SysuiTestCase() { } @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 @@ class CarrierConfigRepositoryTest : SysuiTestCase() { } @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 1fdcf7f27dbf..3ec96908dac8 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 @@ class MobileRepositorySwitcherTest : SysuiTestCase() { } @Test - fun `active repo matches demo mode setting`() = + fun activeRepoMatchesDemoModeSetting() = runBlocking(IMMEDIATE) { whenever(demoModeController.isInDemoMode).thenReturn(false) @@ -177,7 +177,7 @@ class MobileRepositorySwitcherTest : SysuiTestCase() { } @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 47f8cd319bff..1251dfacddfc 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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { /** 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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { } @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 423c47661fa3..9f77744e1fec 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 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() { } @Test - fun `factory - reuses log buffers for same connection`() = + fun factory_reusesLogBuffersForSameConnection() = testScope.runTest { val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock()) @@ -327,7 +327,7 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() { } @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 8d7f0f6035cc..c2768654809b 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 @@ class MobileIconInteractorTest : SysuiTestCase() { } @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 @@ class MobileIconInteractorTest : SysuiTestCase() { } @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 @@ class MobileIconInteractorTest : SysuiTestCase() { } @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 @@ class MobileIconInteractorTest : SysuiTestCase() { } @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 @@ class MobileIconInteractorTest : SysuiTestCase() { } @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 dc683868f727..c84c9c032225 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 @@ class MobileIconsInteractorTest : SysuiTestCase() { // 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 @@ class MobileIconsInteractorTest : SysuiTestCase() { } @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 @@ class MobileIconsInteractorTest : SysuiTestCase() { } @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 @@ class MobileIconsInteractorTest : SysuiTestCase() { } @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 e99be864e73f..d5fb5776b344 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 @@ class LocationBasedMobileIconViewModelTest : SysuiTestCase() { } @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 297cb9d691ba..2b7bc780f7c7 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 @@ class MobileIconViewModelTest : SysuiTestCase() { } @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 @@ class MobileIconViewModelTest : SysuiTestCase() { } @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 @@ class MobileIconViewModelTest : SysuiTestCase() { } @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 @@ class MobileIconViewModelTest : SysuiTestCase() { } @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 @@ class MobileIconViewModelTest : SysuiTestCase() { } @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 f8e1aa94c387..f0458fa83d38 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 @@ class MobileIconsViewModelTest : SysuiTestCase() { } @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 @@ class MobileIconsViewModelTest : SysuiTestCase() { } @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 70d2d2b68460..30b95ef3b205 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 @@ class WifiRepositorySwitcherTest : SysuiTestCase() { } @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 0a3da0b5b029..67727ae21641 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 @@ class BaseUserSwitcherAdapterTest : SysuiTestCase() { } @Test - fun `Adds self to controller in constructor`() { + fun addsSelfToControllerInConstructor() { val captor = kotlinArgumentCaptor<WeakReference<BaseUserSwitcherAdapter>>() verify(controller).addAdapter(captor.capture()) @@ -100,7 +100,7 @@ class BaseUserSwitcherAdapterTest : SysuiTestCase() { } @Test - fun `count - ignores restricted users when device is locked`() { + fun count_ignoresRestrictedUsersWhenDeviceIsLocked() { whenever(controller.isKeyguardShowing).thenReturn(true) users = ArrayList( @@ -131,7 +131,7 @@ class BaseUserSwitcherAdapterTest : SysuiTestCase() { } @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 @@ class BaseUserSwitcherAdapterTest : SysuiTestCase() { } @Test - fun `getName - non guest - returns real name`() { + fun getName_nonGuest_returnsRealName() { val userRecord = createUserRecord( id = 1, @@ -196,7 +196,7 @@ class BaseUserSwitcherAdapterTest : SysuiTestCase() { } @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 @@ class BaseUserSwitcherAdapterTest : SysuiTestCase() { } @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 f14009aad033..70eadcee7607 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,13 +39,33 @@ class UnfoldRemoteFilterTest : SysuiTestCase() { } @Test - fun onTransitionProgress_withInterval_propagated() { + 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.5f) }, + { progressProvider.onTransitionProgress(0.8f) } ) - listener.assertLastProgress(0.5f) + listener.assertLastProgress(0.8f) } @Test 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 e2f3cf7e085f..079fbcd0304c 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 @@ class UserRepositoryImplTest : SysuiTestCase() { } @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 @@ class UserRepositoryImplTest : SysuiTestCase() { 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 0c119fd90c23..948670f95f97 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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @Test - fun `onDeviceBootCompleted - await provisioning - and create guest`() = + fun onDeviceBootCompleted_awaitProvisioning_andCreateGuest() = runBlocking(IMMEDIATE) { setAllowedToAdd(isAllowed = false) underTest.onDeviceBootCompleted() @@ -145,7 +145,7 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @Test - fun `exit - returns to target user`() = + fun exit_returnsToTargetUser() = runBlocking(IMMEDIATE) { repository.setSelectedUserInfo(GUEST_USER_INFO) @@ -182,7 +182,7 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 @@ class GuestUserInteractorTest : SysuiTestCase() { } @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 593ce1f0a2f5..b30f77a663af 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 @@ class RefreshUsersSchedulerTest : SysuiTestCase() { } @Test - fun `pause - prevents the next refresh from happening`() = + fun pause_preventsTheNextRefreshFromHappening() = runBlocking(IMMEDIATE) { underTest = RefreshUsersScheduler( @@ -60,7 +60,7 @@ class RefreshUsersSchedulerTest : SysuiTestCase() { } @Test - fun `unpauseAndRefresh - forces the refresh even when paused`() = + fun unpauseAndRefresh_forcesTheRefreshEvenWhenPaused() = runBlocking(IMMEDIATE) { underTest = RefreshUsersScheduler( @@ -76,7 +76,7 @@ class RefreshUsersSchedulerTest : SysuiTestCase() { } @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 adba53823d2c..d252d5317d06 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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @Test - fun `onRecordSelected - user`() = + fun onRecordSelected_user() = testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -211,7 +211,7 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @Test - fun `onRecordSelected - action`() = + fun onRecordSelected_action() = testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = true) userRepository.setUserInfos(userInfos) @@ -288,7 +288,7 @@ class UserInteractorTest : SysuiTestCase() { } @Test - fun `users - switcher enabled`() = + fun users_switcherEnabled() = testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = true) userRepository.setUserInfos(userInfos) @@ -301,7 +301,7 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @Test - fun `actions - device unlocked`() = + fun actions_deviceUnlocked() = testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) @@ -366,7 +366,7 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @Test - fun `executeAction - navigate to manage users`() = + fun executeAction_navigateToManageUsers() = testScope.runTest { underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) @@ -533,7 +533,7 @@ class UserInteractorTest : SysuiTestCase() { } @Test - fun `executeAction - guest mode`() = + fun executeAction_guestMode() = testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -571,7 +571,7 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @Test - fun `Telephony call state changes - refreshes users`() = + fun telephonyCallStateChanges_refreshesUsers() = testScope.runTest { runCurrent() @@ -639,7 +639,7 @@ class UserInteractorTest : SysuiTestCase() { } @Test - fun `User switched broadcast`() = + fun userSwitchedBroadcast() = testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -670,7 +670,7 @@ class UserInteractorTest : SysuiTestCase() { } @Test - fun `User info changed broadcast`() = + fun userInfoChangedBroadcast() = testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -690,7 +690,7 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 @@ class UserInteractorTest : SysuiTestCase() { } @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 9b74c1f05d71..fd8c6c76720b 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 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { } @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 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { } @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 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { } @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 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { } @Test - fun `should show chip criteria - multiple users`() = + fun shouldShowChipCriteria_multipleUsers() = testScope.runTest { setMultipleUsers() @@ -186,7 +186,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { } @Test - fun `user chip name - shows selected user info`() = + fun userChipName_showsSelectedUserInfo() = testScope.runTest { setMultipleUsers() @@ -206,7 +206,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { } @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 a342dadcb775..91550844ceca 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 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @Test - fun `maximumUserColumns - few users`() = + fun maximumUserColumns_fewUsers() = testScope.runTest { setUsers(count = 2) val values = mutableListOf<Int>() @@ -244,7 +244,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @Test - fun `maximumUserColumns - many users`() = + fun maximumUserColumns_manyUsers() = testScope.runTest { setUsers(count = 5) val values = mutableListOf<Int>() @@ -255,7 +255,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @Test - fun `isOpenMenuButtonVisible - has actions - true`() = + fun isOpenMenuButtonVisible_hasActions_true() = testScope.runTest { setUsers(2) @@ -267,7 +267,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @Test - fun `isOpenMenuButtonVisible - no actions - false`() = + fun isOpenMenuButtonVisible_noActions_false() = testScope.runTest { val userInfos = setUsers(2) userRepository.setSelectedUserInfo(userInfos[1]) @@ -298,7 +298,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @Test - fun `menu actions`() = + fun menuActions() = testScope.runTest { setUsers(2) val actions = mutableListOf<List<UserActionViewModel>>() @@ -318,7 +318,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @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 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @Test - fun `guest selected -- name is exit guest`() = + fun guestSelected_nameIsExitGuest() = testScope.runTest { val userInfos = listOf( @@ -386,7 +386,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @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 9bd3a799350d..3901d720ab3a 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 @@ class WalletContextualSuggestionsControllerTest : SysuiTestCase() { } @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 @@ class WalletContextualSuggestionsControllerTest : SysuiTestCase() { } @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 @@ class WalletContextualSuggestionsControllerTest : SysuiTestCase() { } @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 @@ class WalletContextualSuggestionsControllerTest : SysuiTestCase() { } @Test - fun `state - wallet cards error`() = runTest { + fun state_walletCardsError() = runTest { setUpWalletClient(shouldFail = true) val controller = createWalletContextualSuggestionsController(backgroundScope) val latest = @@ -167,7 +167,7 @@ class WalletContextualSuggestionsControllerTest : SysuiTestCase() { } @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 9a9953865037..bc3a5b7975a7 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_AP 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 @@ public class BubblesTest extends SysuiTestCase { @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 @@ public class BubblesTest extends SysuiTestCase { // 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 @@ public class BubblesTest extends SysuiTestCase { @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 @@ public class BubblesTest extends SysuiTestCase { @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 @@ public class BubblesTest extends SysuiTestCase { // 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 @@ public class BubblesTest extends SysuiTestCase { 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 30418883eaf8..843cc3b78031 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 @@ class UnfoldRemoteFilter( } private var inProgress = false + private var receivedProgressEvent = false private var processedProgress: Float = 1.0f set(newProgress) { @@ -54,7 +55,16 @@ class UnfoldRemoteFilter( 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 @@ class UnfoldRemoteFilter( override fun onTransitionFinished() { inProgress = false + receivedProgressEvent = false listener.onTransitionFinished() } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 0bdb0c80d219..a3b4a0f51c75 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -194,7 +194,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub AccessibilityUserState.ServiceInfoChangeListener, AccessibilityWindowManager.AccessibilityEventSender, AccessibilitySecurityPolicy.AccessibilityUserManager, - SystemActionPerformer.SystemActionsChangedListener, ProxyManager.SystemSupport{ + SystemActionPerformer.SystemActionsChangedListener, + SystemActionPerformer.DisplayUpdateCallBack, ProxyManager.SystemSupport { private static final boolean DEBUG = false; @@ -1219,7 +1220,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private SystemActionPerformer getSystemActionPerformer() { if (mSystemActionPerformer == null) { mSystemActionPerformer = - new SystemActionPerformer(mContext, mWindowManagerService, null, this); + new SystemActionPerformer(mContext, mWindowManagerService, null, this, this); } return mSystemActionPerformer; } @@ -1443,17 +1444,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } if (!mVisibleBgUserIds.get(userId)) { - Slogf.wtf(LOG_TAG, "Cannot change current user to %d as it's not visible " - + "(mVisibleUsers=%s)", userId, mVisibleBgUserIds); + Slogf.wtf(LOG_TAG, "changeCurrentUserForTestAutomationIfNeededLocked(): cannot change " + + "current user to %d as it's not visible (mVisibleUsers=%s)", + userId, mVisibleBgUserIds); return; } if (mCurrentUserId == userId) { - Slogf.w(LOG_TAG, "NOT changing current user for test automation purposes as it is " - + "already %d", mCurrentUserId); + Slogf.d(LOG_TAG, "changeCurrentUserForTestAutomationIfNeededLocked(): NOT changing " + + "current user for test automation purposes as it is already %d", + mCurrentUserId); return; } - Slogf.i(LOG_TAG, "Changing current user from %d to %d for test automation purposes", - mCurrentUserId, userId); + Slogf.i(LOG_TAG, "changeCurrentUserForTestAutomationIfNeededLocked(): changing current user" + + " from %d to %d for test automation purposes", mCurrentUserId, userId); mRealCurrentUserId = mCurrentUserId; switchUser(userId); } @@ -1466,7 +1469,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub + "because device doesn't support visible background users"); return; } - Slogf.i(LOG_TAG, "Restoring current user to %d after using %d for test automation purposes", + if (mRealCurrentUserId == UserHandle.USER_CURRENT) { + Slogf.d(LOG_TAG, "restoreCurrentUserForTestAutomationIfNeededLocked(): ignoring " + + "because mRealCurrentUserId is already USER_CURRENT"); + return; + } + Slogf.i(LOG_TAG, "restoreCurrentUserForTestAutomationIfNeededLocked(): restoring current " + + "user to %d after using %d for test automation purposes", mRealCurrentUserId, mCurrentUserId); int currentUserId = mRealCurrentUserId; mRealCurrentUserId = UserHandle.USER_CURRENT; @@ -1611,6 +1620,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + @Override + // TODO(b/276459590): Remove when this is resolved at the virtual device/input level. + public void moveNonProxyTopFocusedDisplayToTopIfNeeded() { + mA11yWindowManager.moveNonProxyTopFocusedDisplayToTopIfNeeded(); + } + + @Override + // TODO(b/276459590): Remove when this is resolved at the virtual device/input level. + public int getLastNonProxyTopFocusedDisplayId() { + return mA11yWindowManager.getLastNonProxyTopFocusedDisplayId(); + } + @VisibleForTesting void notifySystemActionsChangedLocked(AccessibilityUserState userState) { for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index a8a536590004..78f07e4f2692 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -103,6 +103,9 @@ public class AccessibilityWindowManager { // The top focused display and window token updated with the callback of window lists change. private int mTopFocusedDisplayId; private IBinder mTopFocusedWindowToken; + + // The non-proxy display that most recently had top focus. + private int mLastNonProxyTopFocusedDisplayId; // The display has the accessibility focused window currently. private int mAccessibilityFocusedDisplayId = Display.INVALID_DISPLAY; @@ -451,6 +454,9 @@ public class AccessibilityWindowManager { } if (shouldUpdateWindowsLocked(forceSend, windows)) { mTopFocusedDisplayId = topFocusedDisplayId; + if (!isProxyed(topFocusedDisplayId)) { + mLastNonProxyTopFocusedDisplayId = topFocusedDisplayId; + } mTopFocusedWindowToken = topFocusedWindowToken; if (DEBUG) { Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): updating windows for " @@ -1141,6 +1147,21 @@ public class AccessibilityWindowManager { return false; } + private boolean isProxyed(int displayId) { + final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); + return (observer != null && observer.mIsProxy); + } + + void moveNonProxyTopFocusedDisplayToTopIfNeeded() { + if (mHasProxy + && (mLastNonProxyTopFocusedDisplayId != mTopFocusedDisplayId)) { + mWindowManagerInternal.moveDisplayToTopIfAllowed(mLastNonProxyTopFocusedDisplayId); + } + } + int getLastNonProxyTopFocusedDisplayId() { + return mLastNonProxyTopFocusedDisplayId; + } + /** * Checks if we are tracking windows on specified display. * diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java index c89b9b851742..a13df475d25d 100644 --- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java +++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java @@ -72,6 +72,13 @@ public class SystemActionPerformer { } private final SystemActionsChangedListener mListener; + interface DisplayUpdateCallBack { + void moveNonProxyTopFocusedDisplayToTopIfNeeded(); + + int getLastNonProxyTopFocusedDisplayId(); + } + private final DisplayUpdateCallBack mDisplayUpdateCallBack; + private final Object mSystemActionLock = new Object(); // Resource id based ActionId -> RemoteAction @GuardedBy("mSystemActionLock") @@ -94,7 +101,7 @@ public class SystemActionPerformer { public SystemActionPerformer( Context context, WindowManagerInternal windowManagerInternal) { - this(context, windowManagerInternal, null, null); + this(context, windowManagerInternal, null, null, null); } // Used to mock ScreenshotHelper @@ -103,17 +110,19 @@ public class SystemActionPerformer { Context context, WindowManagerInternal windowManagerInternal, Supplier<ScreenshotHelper> screenshotHelperSupplier) { - this(context, windowManagerInternal, screenshotHelperSupplier, null); + this(context, windowManagerInternal, screenshotHelperSupplier, null, null); } public SystemActionPerformer( Context context, WindowManagerInternal windowManagerInternal, Supplier<ScreenshotHelper> screenshotHelperSupplier, - SystemActionsChangedListener listener) { + SystemActionsChangedListener listener, + DisplayUpdateCallBack callback) { mContext = context; mWindowManagerService = windowManagerInternal; mListener = listener; + mDisplayUpdateCallBack = callback; mScreenshotHelperSupplier = screenshotHelperSupplier; mLegacyHomeAction = new AccessibilityAction( @@ -245,6 +254,7 @@ public class SystemActionPerformer { final long identity = Binder.clearCallingIdentity(); try { synchronized (mSystemActionLock) { + mDisplayUpdateCallBack.moveNonProxyTopFocusedDisplayToTopIfNeeded(); // If a system action is registered with the given actionId, call the corresponding // RemoteAction. RemoteAction registeredAction = mRegisteredSystemActions.get(actionId); @@ -341,7 +351,7 @@ public class SystemActionPerformer { int source) { KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, - source, null); + source, mDisplayUpdateCallBack.getLastNonProxyTopFocusedDisplayId(), null); mContext.getSystemService(InputManager.class) .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); event.recycle(); diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java index eb718853a6ce..d9e25ef7dcdc 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java @@ -199,6 +199,9 @@ public class TouchState { case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: mLastTouchedWindowId = event.getWindowId(); break; + case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END: + mAms.moveNonProxyTopFocusedDisplayToTopIfNeeded(); + break; } } 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 22e742bc0973..c1c47f53ab7e 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 class MagnificationController implements WindowMagnificationManager.Callb */ 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb : 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb } } } 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 2a964b8b701f..3bd4547dd3e6 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.getNumericValue; 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_O 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState int flags) { final FillResponse existingResponse = viewState.getResponse(); mFillRequestEventLogger.startLogForNewRequest(); + mRequestCount++; mFillRequestEventLogger.maybeSetAppPackageUid(uid); mFillRequestEventLogger.maybeSetFlags(mFlags); if(mPreviouslyFillDialogPotentiallyStarted) { @@ -1330,8 +1346,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState this.userId = userId; this.taskId = taskId; this.uid = uid; - mStartTime = SystemClock.elapsedRealtime(); - mLatencyBaseTime = mStartTime; mService = service; mLock = lock; mUi = ui; @@ -1347,11 +1361,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // 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 class Session implements RemoteFillService.FillServiceCallbacks, ViewState } 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mDestroyed) { Slog.w(TAG, "Call to Session#save() rejected - session: " + id + " destroyed"); - mSaveEventLogger.logAndEndEvent(); return; } } @@ -2251,7 +2273,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mDestroyed) { Slog.w(TAG, "Call to Session#cancelSave() rejected - session: " + id + " destroyed"); - mSaveEventLogger.logAndEndEvent(); return; } } @@ -2438,18 +2459,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + authenticationId); logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION); + mPresentationStatsEventLogger.maybeSetAuthenticationResult( + AUTHENTICATION_RESULT_FAILURE); } } else { if (result != null) { @@ -2504,6 +2542,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION); + mPresentationStatsEventLogger.maybeSetAuthenticationResult( + AUTHENTICATION_RESULT_FAILURE); processNullResponseLocked(requestId, 0); } } @@ -4790,6 +4830,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Log FillRequest for Augmented Autofill. mFillRequestEventLogger.startLogForNewRequest(); + mRequestCount++; mFillRequestEventLogger.maybeSetAppPackageUid(uid); mFillRequestEventLogger.maybeSetFlags(mFlags); mFillRequestEventLogger.maybeSetRequestId(AUGMENTED_AUTOFILL_REQUEST_ID); @@ -5036,6 +5077,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // ...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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ @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 92d72ac828f3..541ec80e58ae 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 @@ package com.android.server.autofill; 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 @@ public final class SessionCommittedEventLogger { 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 @@ public final class SessionCommittedEventLogger { 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 b042c3024034..ff72476d4bf1 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 @@ public class FullRestoreEngine extends RestoreEngine { 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 1656b6f0ab9b..77990af50979 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // 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 51d349f542d1..578f5205c414 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -3018,7 +3018,7 @@ public class AccountManagerService final long identityToken = clearCallingIdentity(); try { // Distill the caller's package signatures into a single digest. - final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg); + final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg, userId); // if the caller has permission, do the peek. otherwise go the more expensive // route of starting a Session @@ -3182,12 +3182,12 @@ public class AccountManagerService .write(); } - private byte[] calculatePackageSignatureDigest(String callerPkg) { + private byte[] calculatePackageSignatureDigest(String callerPkg, int userId) { MessageDigest digester; try { digester = MessageDigest.getInstance("SHA-256"); - PackageInfo pkgInfo = mPackageManager.getPackageInfo( - callerPkg, PackageManager.GET_SIGNATURES); + PackageInfo pkgInfo = mPackageManager.getPackageInfoAsUser( + callerPkg, PackageManager.GET_SIGNATURES, userId); for (Signature sig : pkgInfo.signatures) { digester.update(sig.toByteArray()); } @@ -5912,22 +5912,24 @@ public class AccountManagerService } 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 6adccf677bc7..df3c95bdfaf3 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 @@ public final class ActiveServices { // 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 @@ public final class ActiveServices { } } - 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { // "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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { * @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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } } + 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 @@ public final class ActiveServices { } /** + * 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 @@ public final class ActiveServices { 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 44e198b53761..3841b6a27fa0 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 @@ final class ActivityManagerConstants extends ContentObserver { /** @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 @@ final class ActivityManagerConstants extends ContentObserver { 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 @@ final class ActivityManagerConstants extends ContentObserver { 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 @@ final class ActivityManagerConstants extends ContentObserver { 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 97d34b8dd718..1f80aec3d443 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.PrintWriterPrinter; 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 25ac956d328d..f29a2e1ee7da 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.FeatureFlagUtils; 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 @@ public class AppProfiler { 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 @@ public class AppProfiler { } } + /** + * 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 @@ public class AppProfiler { } @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 @@ public class AppProfiler { 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 @@ public class AppProfiler { 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 c3519d21eb15..1e5f187fee4a 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 @@ public class OomAdjuster { 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 312f98ad6e09..d7b22a8a2da0 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.Trace; 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 @@ public final class ProcessList { 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 a875860f016f..78aafeba3b22 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN 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 188341edf512..dbfb832e592a 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 @@ public class AudioService extends IAudioService.Stub 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); } @@ -7704,7 +7704,7 @@ public class AudioService extends IAudioService.Stub 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 @@ -8033,15 +8033,19 @@ public class AudioService extends IAudioService.Stub } 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), @@ -8104,6 +8108,14 @@ public class AudioService extends IAudioService.Stub 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: "); @@ -8246,6 +8258,9 @@ public class AudioService extends IAudioService.Stub */ 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 @@ -8319,6 +8334,17 @@ public class AudioService extends IAudioService.Stub 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) { @@ -8993,7 +9019,7 @@ public class AudioService extends IAudioService.Stub if (streamState.hasValidSettingsName()) { mSettings.putSystemIntForUser(mContentResolver, streamState.getSettingNameForDevice(device), - (streamState.getIndex(device) + 5)/ 10, + (streamState.getIndex(device) + 5) / 10, UserHandle.USER_CURRENT); } } @@ -12456,20 +12482,16 @@ public class AudioService extends IAudioService.Stub } int addMixes(@NonNull ArrayList<AudioMix> mixes) { - // TODO optimize to not have to unregister the mixes already in place synchronized (mMixes) { - mAudioSystem.registerPolicyMixes(mMixes, false); this.add(mixes); - return mAudioSystem.registerPolicyMixes(mMixes, true); + return mAudioSystem.registerPolicyMixes(mixes, true); } } int removeMixes(@NonNull ArrayList<AudioMix> mixes) { - // TODO optimize to not have to unregister the mixes already in place synchronized (mMixes) { - mAudioSystem.registerPolicyMixes(mMixes, false); this.remove(mixes); - return mAudioSystem.registerPolicyMixes(mMixes, true); + return mAudioSystem.registerPolicyMixes(mixes, false); } } 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 a48a9d1c3eaa..cd2a26fbd3e4 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 @@ class AuthResultCoordinator { */ 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 @@ class AuthResultCoordinator { * 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 1aee5d48b9ee..2653ce76459d 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 @@ public class AuthSessionCoordinator implements AuthSessionListener { 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 @@ public class AuthSessionCoordinator implements AuthSessionListener { mAuthResultCoordinator = new AuthResultCoordinator(); mMultiBiometricLockoutState = new MultiBiometricLockoutState(clock); mRingBuffer = new RingBuffer(100); - mTimedLockouts = new ArrayList<>(); - mClock = clock; } /** @@ -74,8 +68,6 @@ public class AuthSessionCoordinator implements AuthSessionListener { mAuthOperations.clear(); mUserId = userId; mIsAuthenticating = true; - mAuthOperations.clear(); - mTimedLockouts.clear(); mAuthResultCoordinator = new AuthResultCoordinator(); mRingBuffer.addApiCall("internal : onAuthSessionStarted(" + userId + ")"); } @@ -85,39 +77,30 @@ public class AuthSessionCoordinator implements AuthSessionListener { * * 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.setAuthenticatorTo(mUserId, authenticator, - true /* canAuthenticate */); - mMultiBiometricLockoutState.clearLockoutTime(mUserId, authenticator); - } else if ((value & AUTHENTICATOR_LOCKED) == AUTHENTICATOR_LOCKED) { - mMultiBiometricLockoutState.setAuthenticatorTo(mUserId, authenticator, - false /* canAuthenticate */); - } - + // 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); } - - 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 @@ public class AuthSessionCoordinator implements AuthSessionListener { + ", 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 @@ public class AuthSessionCoordinator implements AuthSessionListener { // 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 c24a98940519..45933fe748dc 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 @@ class MultiBiometricLockoutState { 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 @@ class MultiBiometricLockoutState { 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 @@ class MultiBiometricLockoutState { 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 @@ class MultiBiometricLockoutState { 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 759c52a45807..1a12fcdf5010 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.BiometricsProto; 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 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem 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 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem */ 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 0d30ddd56699..468bf5530d34 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.UserStateProto; 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 @@ public class Sensor { private final LockoutCache mLockoutCache; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; + + @NonNull + private AuthSessionCoordinator mAuthSessionCoordinator; @NonNull private final Callback mCallback; @@ -134,6 +138,7 @@ public class Sensor { @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 @@ public class Sensor { mUserId = userId; mLockoutCache = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; + mAuthSessionCoordinator = authSessionCoordinator; mCallback = callback; } @@ -346,8 +352,12 @@ public class Sensor { 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 @@ public class Sensor { 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 0b2421b745f5..7a620349075c 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.BiometricsProto; 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 @@ class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implem 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 @@ class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implem */ 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 1dcf4e9528d2..22ca816ba56c 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.Utils; 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 @@ public class Sensor { @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 @@ public class Sensor { mUserId = userId; mLockoutCache = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; + mAuthSessionCoordinator = authSessionCoordinator; mCallback = callback; } @@ -327,8 +332,12 @@ public class Sensor { 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 @@ public class Sensor { 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 1f4c7e6fa4e5..1dc2725feb1b 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.VpnConfig; 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 @@ public class Vpn { * <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 @@ public class Vpn { /** * 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { } /** - * 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 @@ public class Vpn { * {@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 @@ public class Vpn { mAlwaysOn = false; } + final boolean oldLockdownState = mLockdown; mLockdown = (mAlwaysOn && lockdown); mLockdownAllowlist = (mLockdown && lockdownAllowlist != null) ? Collections.unmodifiableList(new ArrayList<>(lockdownAllowlist)) @@ -1118,6 +1156,13 @@ public class Vpn { 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 @@ public class Vpn { // 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 @@ public class Vpn { 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 @@ public class Vpn { // 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 @@ public class Vpn { || 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 @@ public class Vpn { */ 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { } } - 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 @@ public class Vpn { // 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { } 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 ab261ac24091..f4c84e7fe464 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.Slog; 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 @@ class DeviceStateNotificationController extends BroadcastReceiver { 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 @@ class DeviceStateNotificationController extends BroadcastReceiver { 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 @@ class DeviceStateNotificationController extends BroadcastReceiver { * @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 @@ class DeviceStateNotificationController extends BroadcastReceiver { * @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 @@ class DeviceStateNotificationController extends BroadcastReceiver { * @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 @@ class DeviceStateNotificationController extends BroadcastReceiver { * @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 @@ class DeviceStateNotificationController extends BroadcastReceiver { 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<>(); - - 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); - - - 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!"); + private SparseArray<NotificationInfo> getNotificationInfos() { + Locale locale = mContext.getResources().getConfiguration().getLocales().get(0); + return mNotificationInfoProvider.getNotificationInfos(locale); + } + + @VisibleForTesting + public static class NotificationInfoProvider { + @NonNull + private final Context mContext; + private final Object mLock = new Object(); + + @GuardedBy("mLock") + @Nullable + private SparseArray<NotificationInfo> mCachedNotificationInfos; + + @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; } + } - notificationInfos.put( - identifier, - new NotificationInfo( - names[i], activeNotificationTitles[i], activeNotificationContents[i], - thermalCriticalNotificationTitles[i], - thermalCriticalNotificationContents[i], - powerSaveModeNotificationTitles[i], - powerSaveModeNotificationContents[i]) - ); + + @VisibleForTesting + Locale getCachedLocale() { + synchronized (mLock) { + return mCachedLocale; + } + } + + @VisibleForTesting + public void refreshNotificationInfos(Locale locale) { + synchronized (mLock) { + mCachedLocale = locale; + mCachedNotificationInfos = loadNotificationInfos(); + } } - return notificationInfos; + @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!"); + } + + for (int i = 0; i < stateIdentifiers.length; i++) { + int identifier = stateIdentifiers[i]; + if (identifier == DeviceStateManager.INVALID_DEVICE_STATE) { + continue; + } + + 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 85b403496645..5d92c7f8cf05 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.os.UserManager; 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { // 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 5e3990ac7167..1acc20880258 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 class DisplayPowerController implements AutomaticBrightnessController.Call 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mIsEnabled = isEnabled; mIsInTransition = isInTransition; } - + mIsDisplayInternal = isDisplayInternal; if (changed) { updatePowerState(); } @@ -1810,10 +1816,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 23e606c028ac..b36aedefa2a7 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 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal 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 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal .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 class DisplayPowerController2 implements AutomaticBrightnessController.Cal 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 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mIsInTransition = isInTransition; } + mIsDisplayInternal = isDisplayInternal; if (changed) { updatePowerState(); } @@ -1449,10 +1456,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // 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 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal 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 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal 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 000000000000..76702d3f6f8c --- /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 6511f4f4fa84..1e13974f6ef4 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 final class WakelockController { 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 @@ public final class WakelockController { * 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 01cae42ea7aa..ad640b18b80c 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 @@ public final class FontManagerService extends IFontManager.Stub { 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 @@ public final class FontManagerService extends IFontManager.Stub { } /** - * 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 c039a836843c..6d82841da3a4 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 @@ public class FontManagerShellCommand extends ShellCommand { 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 9eedc4e0f9ee..f47c4b2c24d9 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 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly private void launchDeviceDiscovery() { assertRunOnServiceThread(); - clearDeviceInfoList(); DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, new DeviceDiscoveryCallback() { @Override @@ -691,13 +690,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { 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 805ff6611c29..75fe63a66206 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 @@ public class HdmiControlService extends SystemService { // 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 @@ public class HdmiControlService extends SystemService { 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 0466d8a88053..4fea05d295b6 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 d0669e7602ed..5f45f912a87a 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 @@ public class InputManagerService extends IInputManager.Stub @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 @@ public class InputManagerService extends IInputManager.Stub 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 0ae1e8076b81..a1b67e105dd4 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 @@ package com.android.server.inputmethod; 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.annotations.GuardedBy; 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 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { 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 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { 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 f03e985c07e9..27f6a89a73b3 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 @@ interface ImeVisibilityApplier { // 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 61fe6545f139..19d6fa00a270 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.SOFT_INPUT_STATE_UNSPECIFI 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 android.view.inputmethod.InputMethodManager; 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 @@ public final class ImeVisibilityStateComputer { */ 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 final class ImeVisibilityStateComputer { 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 @@ public final class ImeVisibilityStateComputer { STATE_HIDE_IME_EXPLICIT, STATE_HIDE_IME_NOT_ALWAYS, STATE_SHOW_IME_IMPLICIT, + STATE_REMOVE_IME_SNAPSHOT, }) @interface VisibilityState {} @@ -172,6 +192,24 @@ public final class ImeVisibilityStateComputer { 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 @@ public final class ImeVisibilityStateComputer { 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 24332112ed76..c70d55510493 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 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } + 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 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub 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/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index a7e704e1793b..20f06971b6b2 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -401,11 +401,13 @@ public class LockSettingsService extends ILockSettings.Stub { profileUserId, /* isLockTiedToParent= */ true); return; } + final long parentSid; // Do not tie when the parent has no SID (but does have a screen lock). // This can only happen during an upgrade path where SID is yet to be // generated when the user unlocks for the first time. try { - if (getGateKeeperService().getSecureUserId(parentId) == 0) { + parentSid = getGateKeeperService().getSecureUserId(parentId); + if (parentSid == 0) { return; } } catch (RemoteException e) { @@ -416,7 +418,8 @@ public class LockSettingsService extends ILockSettings.Stub { setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId, /* isLockTiedToParent= */ true); tieProfileLockToParent(profileUserId, parentId, unifiedProfilePassword); - mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword); + mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, + parentSid); } } @@ -575,7 +578,7 @@ public class LockSettingsService extends ILockSettings.Stub { public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache( java.security.KeyStore ks) { - return new ManagedProfilePasswordCache(ks, getUserManager()); + return new ManagedProfilePasswordCache(ks); } public boolean isHeadlessSystemUserMode() { @@ -1346,7 +1349,13 @@ public class LockSettingsService extends ILockSettings.Stub { LockscreenCredential credential = LockscreenCredential.createManagedPassword( decryptionResult); Arrays.fill(decryptionResult, (byte) 0); - mManagedProfilePasswordCache.storePassword(userId, credential); + try { + long parentSid = getGateKeeperService().getSecureUserId( + mUserManager.getProfileParent(userId).id); + mManagedProfilePasswordCache.storePassword(userId, credential, parentSid); + } catch (RemoteException e) { + Slogf.w(TAG, "Failed to talk to GateKeeper service", e); + } return credential; } @@ -2224,6 +2233,8 @@ public class LockSettingsService extends ILockSettings.Stub { public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential, int userId, @LockPatternUtils.VerifyFlag int flags) { checkPasswordReadPermission(); + Slogf.i(TAG, "Verifying tied profile challenge for user %d", userId); + if (!isProfileWithUnifiedLock(userId)) { throw new IllegalArgumentException( "User id must be managed/clone profile with unified lock"); diff --git a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java b/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java index ddc0e5424421..1298fe8f07a4 100644 --- a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java +++ b/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java @@ -17,9 +17,7 @@ package com.android.server.locksettings; import android.annotation.Nullable; -import android.content.pm.UserInfo; -import android.os.UserHandle; -import android.os.UserManager; +import android.security.GateKeeper; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.security.keystore.UserNotAuthenticatedException; @@ -45,12 +43,9 @@ import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; /** - * Caches *unified* work challenge for user 0's managed profiles. Only user 0's profile is supported - * at the moment because the cached credential is encrypted using a keystore key auth-bound to - * user 0: this is to match how unified work challenge is similarly auth-bound to its parent user's - * lockscreen credential normally. It's possible to extend this class to support managed profiles - * for secondary users, that will require generating auth-bound keys to their corresponding parent - * user though (which {@link KeyGenParameterSpec} does not support right now). + * Caches *unified* work challenge for managed profiles. The cached credential is encrypted using + * a keystore key auth-bound to the parent user's lockscreen credential, similar to how unified + * work challenge is normally secured. * * <p> The cache is filled whenever the managed profile's unified challenge is created or derived * (as part of the parent user's credential verification flow). It's removed when the profile is @@ -70,28 +65,23 @@ public class ManagedProfilePasswordCache { private final SparseArray<byte[]> mEncryptedPasswords = new SparseArray<>(); private final KeyStore mKeyStore; - private final UserManager mUserManager; - public ManagedProfilePasswordCache(KeyStore keyStore, UserManager userManager) { + public ManagedProfilePasswordCache(KeyStore keyStore) { mKeyStore = keyStore; - mUserManager = userManager; } /** * Encrypt and store the password in the cache. Does NOT overwrite existing password cache * if one for the given user already exists. + * + * Should only be called on a profile userId. */ - public void storePassword(int userId, LockscreenCredential password) { + public void storePassword(int userId, LockscreenCredential password, long parentSid) { + if (parentSid == GateKeeper.INVALID_SECURE_USER_ID) return; synchronized (mEncryptedPasswords) { if (mEncryptedPasswords.contains(userId)) { return; } - UserInfo parent = mUserManager.getProfileParent(userId); - if (parent == null || parent.id != UserHandle.USER_SYSTEM) { - // Since the cached password is encrypted using a keystore key auth-bound to user 0, - // only support caching password for user 0's profile. - return; - } String keyName = getEncryptionKeyName(userId); KeyGenerator generator; SecretKey key; @@ -104,8 +94,8 @@ public class ManagedProfilePasswordCache { .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setNamespace(SyntheticPasswordCrypto.keyNamespace()) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) - // Generate auth-bound key to user 0 (since we the caller is user 0) .setUserAuthenticationRequired(true) + .setBoundToSpecificSecureUserId(parentSid) .setUserAuthenticationValidityDurationSeconds(CACHE_TIMEOUT_SECONDS) .build()); key = generator.generateKey(); diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index b82e3a31567e..c076c0574afe 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 @@ package com.android.server.media; 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 android.os.Bundle; 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 @@ abstract class MediaRoute2Provider { && 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 90451b19d590..72b843672ae8 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 android.util.Slog; 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 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider 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 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } @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 3c97aaf87e9c..2d3b97b768d3 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 @@ class MediaRouter2ServiceImpl { 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 5ea2ca4fc2f2..464a256bd30e 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.ParcelableListBinder; 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.Process; 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 @@ import java.util.concurrent.CopyOnWriteArrayList; */ // 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } }; + @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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } @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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR != 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 5d5c621eb3f5..6d2d2e405ab9 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 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { 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 ebcbfed93ac7..07891f30abd0 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { @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 class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { } 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 @@ public class NotificationManagerService extends SystemService { @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); - - 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); + 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); + + 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 c1171fabea7b..2704f56b539d 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.Context; 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 @@ public final class BroadcastHelper { 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 @@ public final class BroadcastHelper { } /** 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/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 89d2d12409be..1e0c95ca9013 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 @@ final class InstallPackageHelper { // 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 @@ final class InstallPackageHelper { } // 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 ccade60af53e..b5108af6d2f8 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 @@ public class PackageManagerService implements PackageSender, TestUtilityService // 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 5015985dd747..7198de2f743e 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 @@ final class VerifyingSession { 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 86403779d35a..0ce17ded31e4 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 @@ public class StatsPullAtomService extends SystemService { 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 @@ public class StatsPullAtomService extends SystemService { registerPendingIntentsPerPackagePuller(); registerPinnerServiceStats(); registerHdrCapabilitiesPuller(); + registerCachedAppsHighWatermarkPuller(); } private void initAndRegisterNetworkStatsPullers() { @@ -4732,6 +4735,12 @@ public class StatsPullAtomService extends SystemService { 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 @@ public class StatsPullAtomService extends SystemService { ); } + 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/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index 984cb19cbbf9..31348cd9156f 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -42,7 +42,6 @@ import android.service.textclassifier.ITextClassifierService; import android.service.textclassifier.TextClassifierService; import android.service.textclassifier.TextClassifierService.ConnectionState; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.LruCache; import android.util.Slog; import android.util.SparseArray; @@ -74,7 +73,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; /** @@ -388,14 +387,13 @@ public final class TextClassificationManagerService extends ITextClassifierServi synchronized (mLock) { final StrippedTextClassificationContext textClassificationContext = - mSessionCache.get(sessionId); + mSessionCache.get(sessionId.getToken()); final int userId = textClassificationContext != null ? textClassificationContext.userId : UserHandle.getCallingUserId(); final boolean useDefaultTextClassifier = - textClassificationContext != null - ? textClassificationContext.useDefaultTextClassifier - : true; + textClassificationContext == null + || textClassificationContext.useDefaultTextClassifier; final SystemTextClassifierMetadata sysTcMetadata = new SystemTextClassifierMetadata( "", userId, useDefaultTextClassifier); @@ -405,7 +403,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi /* attemptToBind= */ false, service -> { service.onDestroyTextClassificationSession(sessionId); - mSessionCache.remove(sessionId); + mSessionCache.remove(sessionId.getToken()); }, "onDestroyTextClassificationSession", NO_OP_CALLBACK); @@ -676,14 +674,39 @@ public final class TextClassificationManagerService extends ITextClassifierServi @NonNull private final Object mLock; + @NonNull - @GuardedBy("mLock") - private final LruCache<TextClassificationSessionId, StrippedTextClassificationContext> - mCache = new LruCache<>(MAX_CACHE_SIZE); + private final DeathRecipient mDeathRecipient = new DeathRecipient() { + @Override + public void binderDied() { + // no-op + } + + @Override + public void binderDied(IBinder who) { + if (DEBUG) { + Slog.d(LOG_TAG, "binderDied for " + who); + } + remove(who); + } + }; @NonNull @GuardedBy("mLock") - private final Map<TextClassificationSessionId, DeathRecipient> mDeathRecipients = - new ArrayMap<>(); + private final LruCache<IBinder, StrippedTextClassificationContext> + mCache = new LruCache<>(MAX_CACHE_SIZE) { + @Override + protected void entryRemoved(boolean evicted, + IBinder token, + StrippedTextClassificationContext oldValue, + StrippedTextClassificationContext newValue) { + if (evicted) { + // The remove(K) or put(K, V) should be handled + token.unlinkToDeath(mDeathRecipient, /* flags= */ 0); + // TODO(b/278160706): handle app process and TCS's behavior if the + // session is removed by system server + } + } + }; SessionCache(@NonNull Object lock) { mLock = Objects.requireNonNull(lock); @@ -692,12 +715,10 @@ public final class TextClassificationManagerService extends ITextClassifierServi void put(@NonNull TextClassificationSessionId sessionId, @NonNull TextClassificationContext textClassificationContext) { synchronized (mLock) { - mCache.put(sessionId, + mCache.put(sessionId.getToken(), new StrippedTextClassificationContext(textClassificationContext)); try { - DeathRecipient deathRecipient = () -> remove(sessionId); - sessionId.getToken().linkToDeath(deathRecipient, /* flags= */ 0); - mDeathRecipients.put(sessionId, deathRecipient); + sessionId.getToken().linkToDeath(mDeathRecipient, /* flags= */ 0); } catch (RemoteException e) { Slog.w(LOG_TAG, "SessionCache: Failed to link to death", e); } @@ -705,22 +726,29 @@ public final class TextClassificationManagerService extends ITextClassifierServi } @Nullable - StrippedTextClassificationContext get(@NonNull TextClassificationSessionId sessionId) { - Objects.requireNonNull(sessionId); + StrippedTextClassificationContext get(@NonNull IBinder token) { + Objects.requireNonNull(token); synchronized (mLock) { - return mCache.get(sessionId); + return mCache.get(token); } } - void remove(@NonNull TextClassificationSessionId sessionId) { - Objects.requireNonNull(sessionId); + void remove(@NonNull IBinder token) { + Objects.requireNonNull(token); synchronized (mLock) { - DeathRecipient deathRecipient = mDeathRecipients.get(sessionId); - if (deathRecipient != null) { - sessionId.getToken().unlinkToDeath(deathRecipient, /* flags= */ 0); + if (DEBUG) { + Slog.d(LOG_TAG, "SessionCache: remove for " + token); + } + if (token != null) { + try { + token.unlinkToDeath(mDeathRecipient, /* flags= */ 0); + } catch (NoSuchElementException e) { + if (DEBUG) { + Slog.d(LOG_TAG, "SessionCache: " + token + " was already unlinked."); + } + } } - mDeathRecipients.remove(sessionId); - mCache.remove(sessionId); + mCache.remove(token); } } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 2d3928ca5721..8f416082374e 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -2237,7 +2237,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde } clearAllResourcesAndClientMapping(getClientProfile(clientId)); mClientProfiles.remove(clientId); - mListeners.remove(clientId); + + // it may be called by unregisterClientProfileInternal under test + synchronized (mLock) { + ResourcesReclaimListenerRecord record = mListeners.remove(clientId); + if (record != null) { + record.getListener().asBinder().unlinkToDeath(record, 0); + } + } } private void clearFrontendAndClientMapping(ClientProfile profile) { diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 1ab7f362808a..9cf08340f613 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 @@ final class VibrationSettings { 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 a757d90b75ba..f71f3b128557 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 @@ class ActivityMetricsLogger { /** 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 f5cb613601fe..c6a2e0e51227 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + @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 class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 5f56af7fd4e0..1208b6ef396f 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 class ActivityServiceConnectionsHolder<T> { } 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 1944b3f8e6d3..90af4c6236aa 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_IMMUTABLE; 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 @@ class ActivityStartInterceptor { @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 @@ class ActivityStartInterceptor { @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 c5e75faf2c6c..a27f3e49457d 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 @@ class ActivityStarter { // 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 401c16f7f76a..f93afe81f804 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.INVALID_TASK_ID; 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 597c8bf45132..805bff240f66 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 @@ public class AppTransitionController { 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 11d84ffbd064..0c196d7e99e9 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 @@ class BackNavigationController { 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 a83a033985c5..3dc3be9abf74 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 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl 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 c2bc4591ce0d..bad64d357b13 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ @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 339b6ec30049..747819e93ff2 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 000000000000..8bc445bc97bb --- /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 71dd91785384..1d9f24c4b317 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 @@ package com.android.server.wm; 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 @@ public abstract class ImeTargetVisibilityPolicy { * @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/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index ff1deaf415b3..6ef6fa7db740 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -48,9 +48,9 @@ import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTA import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH; import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; -import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM; @@ -236,7 +236,7 @@ final class LetterboxUiController { private final Boolean mBooleanPropertyIgnoreRequestedOrientation; @Nullable - private final Boolean mBooleanPropertyIgnoreOrientationRequestWhenLoopDetected; + private final Boolean mBooleanPropertyAllowIgnoringOrientationRequestWhenLoopDetected; @Nullable private final Boolean mBooleanPropertyFakeFocus; @@ -259,10 +259,10 @@ final class LetterboxUiController { readComponentProperty(packageManager, mActivityRecord.packageName, mLetterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled, PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION); - mBooleanPropertyIgnoreOrientationRequestWhenLoopDetected = + mBooleanPropertyAllowIgnoringOrientationRequestWhenLoopDetected = readComponentProperty(packageManager, mActivityRecord.packageName, mLetterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled, - PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED); + PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED); mBooleanPropertyFakeFocus = readComponentProperty(packageManager, mActivityRecord.packageName, mLetterboxConfiguration::isCompatFakeFocusEnabled, @@ -445,7 +445,7 @@ final class LetterboxUiController { /* gatingCondition */ mLetterboxConfiguration ::isPolicyForIgnoringRequestedOrientationEnabled, mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled, - mBooleanPropertyIgnoreOrientationRequestWhenLoopDetected)) { + mBooleanPropertyAllowIgnoringOrientationRequestWhenLoopDetected)) { return false; } diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java index 300a894d15c9..cff86add7efc 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 @@ public abstract class StartingData { /** 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 2e5ab1a7bfe1..7572a6417f5c 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 @@ class SurfaceAnimationRunner { 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 9363eb5cefc6..5c33e6470024 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 @@ class Task extends TaskFragment { 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 @@ class Task extends TaskFragment { 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 b0a879e96dcf..e80cbb302424 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 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { 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 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { 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 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // 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 c09962843890..8c798759c890 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 @@ import java.util.HashMap; 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 @@ final class TaskFpsCallbackController { } 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; + } + + IBinder binder = callback.asBinder(); + if (!mTaskFpsCallbacks.containsKey(binder)) { return; } - callback.asBinder().unlinkToDeath(mDeathRecipients.get(callback), 0); - mDeathRecipients.remove(callback); + binder.unlinkToDeath(mDeathRecipients.get(binder), 0); + mDeathRecipients.remove(binder); - nativeUnregister(mTaskFpsCallbacks.get(callback)); - mTaskFpsCallbacks.remove(callback); + 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 184293e11002..5626aa7f075f 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 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { 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 969afe544b18..792ec2e92083 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -444,6 +444,11 @@ public abstract class WindowManagerInternal { public abstract IBinder getFocusedWindowTokenFromWindowStates(); /** + * Moves the given display to the top. + */ + public abstract void moveDisplayToTopIfAllowed(int displayId); + + /** * @return Whether the keyguard is engaged. */ public abstract boolean isKeyguardLocked(); @@ -848,6 +853,16 @@ public abstract class WindowManagerInternal { } /** + * 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 99d0ea86e2f4..8822193ab522 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 @@ public class WindowManagerService extends IWindowManager.Stub boolean mHardKeyboardAvailable; WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; + + @Nullable ImeTargetChangeListener mImeTargetChangeListener; + SettingsObserver mSettingsObserver; final EmbeddedWindowController mEmbeddedWindowController; final AnrController mAnrController; @@ -1807,6 +1810,10 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.mSurfaceController.setSecure(win.isSecureLocked()); } + final boolean wasVisible = win.isVisible(); + win.mRelayoutCalled = true; win.mInRelayout = true; @@ -2336,7 +2345,6 @@ public class WindowManagerService extends IWindowManager.Stub "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 @@ public class WindowManagerService extends IWindowManager.Stub } 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 @@ public class WindowManagerService extends IWindowManager.Stub }); } + 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, @@ -7672,6 +7716,11 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void moveDisplayToTopIfAllowed(int displayId) { + WindowManagerService.this.moveDisplayToTopIfAllowed(displayId); + } + + @Override public boolean isKeyguardLocked() { return WindowManagerService.this.isKeyguardLocked(); } @@ -8262,13 +8311,19 @@ public class WindowManagerService extends IWindowManager.Stub } 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 +8339,18 @@ public class WindowManagerService extends IWindowManager.Stub 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 d1618e9a278b..a29959297dc7 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_ON_SCREEN; 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP dc.computeImeTarget(true /* updateImeTarget */); } if (dc.getImeInputTarget() == this && !inRelaunchingActivity()) { + mWmService.dispatchImeInputTargetVisibilityChanged(mClient.asBinder(), + false /* visible */, true /* removed */); dc.updateImeInputAndControlTarget(null); } @@ -4027,6 +4035,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP 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 ee73f8afabd2..82f9aadba9f4 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 @@ final class BundlePolicySerializer extends PolicySerializer<Bundle> { 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; - } - while (parser.next() != XmlPullParser.END_DOCUMENT) { - readEntry(restrictions, values, parser); + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + readBundle(bundle, 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 @@ final class BundlePolicySerializer extends PolicySerializer<Bundle> { 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 702602adf1b3..415440b1f46d 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 @@ final class DevicePolicyEngine { } /** + * 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 @@ final class DevicePolicyEngine { 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 @@ final class DevicePolicyEngine { 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 855734803c02..7e5d5aae06e4 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.REQUEST_PASSWORD_COMPLEXITY; 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.Notification; 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.Map; 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } } + + if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { + calculateHasIncompatibleAccounts(); + } + if (Intent.ACTION_BOOT_COMPLETED.equals(action) && userHandle == mOwners.getDeviceOwnerUserId()) { mBugreportCollectionManager.checkForPendingBugreportAfterBoot(); @@ -1252,6 +1260,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener()); mDeviceManagementResourcesProvider.load(); - if (isPermissionCheckFlagEnabled()) { + if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) { mDevicePolicyEngine.load(); } @@ -3279,8 +3290,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } startOwnerService(userId, "start-user"); - if (isPermissionCheckFlagEnabled()) { + if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) { mDevicePolicyEngine.handleStartUser(userId); } } @@ -3619,7 +3632,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { void handleUnlockUser(int userId) { startOwnerService(userId, "unlock-user"); - if (isPermissionCheckFlagEnabled()) { + if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) { mDevicePolicyEngine.handleUnlockUser(userId); } } @@ -3631,7 +3644,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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); } + Set<PolicyKey> keys = mDevicePolicyEngine.getLocalPolicyKeysSetByAllAdmins( + PolicyDefinition.GENERIC_ACCOUNT_MANAGEMENT_DISABLED, + affectedUser); + + 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()); + + Boolean disabled = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType), + affectedUser); + if (disabled != null && disabled) { + resultSet.add(accountType); + } + } + } else { caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId)); - } - - synchronized (getLockObject()) { - final ArraySet<String> resultSet = new ArraySet<>(); - if (!parent) { - final DevicePolicyData policy = getUserData(userId); - for (ActiveAdmin admin : policy.mAdminList) { - resultSet.addAll(admin.accountTypesWithManagementDisabled); + 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); + // 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - boolean compatible = !hasIncompatibleAccounts(am, accounts); + boolean compatible = !hasIncompatibleAccounts(userId); if (compatible) { Slogf.w(LOG_TAG, "All accounts are compatible"); } else { @@ -18394,37 +18474,67 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { }); } - 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 }; + @Override + public void calculateHasIncompatibleAccounts() { + new CalculateHasIncompatibleAccountsTask().executeOnExecutor( + AsyncTask.THREAD_POOL_EXECUTOR, null); + } - 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; + @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; } - return false; - } + 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; + } + } - 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); 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { "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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ); /** + * 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 8c2468af1146..638596b5cc20 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 @@ package com.android.server.devicepolicy; 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 @@ final class PolicyDefinition<V> { 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 @@ final class PolicyDefinition<V> { 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 7d4f87d73507..a6ada4d77253 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 static org.mockito.Mockito.verify; 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 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe } } + @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 2a256f262980..3871e1dfd5b0 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_HIDE; 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 android.view.inputmethod.InputMethodManager; 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 @@ import org.junit.runner.RunWith; @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 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes 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 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes 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 90691a75aede..c80ecbf62142 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.SystemServerInitThreadPool; 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 @@ public class InputMethodManagerServiceTestBase { @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 @@ public class InputMethodManagerServiceTestBase { .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 @@ public class InputMethodManagerServiceTestBase { 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/InputMethodSystemServerTests/test-apps/SimpleTestIme/res/values/dimens.xml b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/res/values/dimens.xml index 1a4959e14a1f..5d91bd20c77f 100644 --- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/res/values/dimens.xml +++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/res/values/dimens.xml @@ -16,9 +16,9 @@ --> <resources> - <dimen name="text_size_normal">24dp</dimen> + <dimen name="text_size_normal">20dp</dimen> <dimen name="text_size_symbol">14dp</dimen> - <dimen name="keyboard_header_height">40dp</dimen> - <dimen name="keyboard_row_height">50dp</dimen> + <dimen name="keyboard_header_height">30dp</dimen> + <dimen name="keyboard_row_height">40dp</dimen> </resources>
\ No newline at end of file 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 8c8401497d48..dc92376263a6 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 class PackageManagerTests extends AndroidTestCase { } 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 000000000000..d9fbba5b4274 --- /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 07a81ffb8e18..c23d4b19cd3a 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 @@ public final class WakelockControllerTest { 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 @@ public final class WakelockControllerTest { assertFalse(mWakelockController.releaseWakelock(wakelockId)); assertFalse(isWakelockAcquiredCallable.call()); } - - } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index 8f38f25b2f62..9cd22dd292a5 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -1132,7 +1132,7 @@ public class JobSchedulerServiceTest { @Test public void testRareJobBatching() { spyOn(mService); - doNothing().when(mService).evaluateControllerStatesLocked(any()); + doReturn(false).when(mService).evaluateControllerStatesLocked(any()); doNothing().when(mService).noteJobsPending(any()); doReturn(true).when(mService).isReadyToBeExecutedLocked(any(), anyBoolean()); advanceElapsedClock(24 * HOUR_IN_MILLIS); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java index d9461aada4d3..b62dbcd526cb 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java @@ -108,6 +108,7 @@ public class SystemActionPerformerTest { @Mock private StatusBarManager mMockStatusBarManager; @Mock private ScreenshotHelper mMockScreenshotHelper; @Mock private SystemActionPerformer.SystemActionsChangedListener mMockListener; + @Mock private SystemActionPerformer.DisplayUpdateCallBack mMockCallback; @Before public void setup() { @@ -125,7 +126,7 @@ public class SystemActionPerformerTest { mMockContext, mMockWindowManagerInternal, () -> mMockScreenshotHelper, - mMockListener); + mMockListener, mMockCallback); } private void setupWithRealContext() { @@ -133,7 +134,7 @@ public class SystemActionPerformerTest { InstrumentationRegistry.getContext(), mMockWindowManagerInternal, () -> mMockScreenshotHelper, - mMockListener); + mMockListener, mMockCallback); } // We need below two help functions because AccessbilityAction.equals function only compares 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 913d8c1dda20..11e4120e77e6 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.ArgumentMatchers.nullable; 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { } @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 ebf7fd8c2752..2102b0962bbf 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 @@ public class AuthResultCoordinatorTest { 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 @@ public class AuthResultCoordinatorTest { @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_PERMANENT_LOCKED); + assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo( + 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_LOCKED); + AUTHENTICATOR_TIMED_LOCKED); } @Test @@ -132,13 +163,27 @@ public class AuthResultCoordinatorTest { 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 @@ public class AuthResultCoordinatorTest { 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 c3b9cb17ead8..f26c7e63a273 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 @@ public class AuthSessionCoordinatorTest { 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 @@ public class AuthSessionCoordinatorTest { 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 968844e4c990..c28de55c7299 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 @@ public class MultiBiometricLockoutStateTest { 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 @@ public class MultiBiometricLockoutStateTest { } 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 @@ public class MultiBiometricLockoutStateTest { @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 @@ public class MultiBiometricLockoutStateTest { @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 @@ public class MultiBiometricLockoutStateTest { @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 @@ public class MultiBiometricLockoutStateTest { @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 @@ public class MultiBiometricLockoutStateTest { @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 @@ public class MultiBiometricLockoutStateTest { @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 @@ public class MultiBiometricLockoutStateTest { 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 @@ public class MultiBiometricLockoutStateTest { 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 @@ public class MultiBiometricLockoutStateTest { 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 @@ public class MultiBiometricLockoutStateTest { 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 1f29becf5cc2..9c9d3f894d95 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 @@ package com.android.server.biometrics.sensors.face.aidl; 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 @@ public class SensorTest { 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 @@ public class SensorTest { 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 7ae4e17e394d..0c1346696b58 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 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; 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 @@ public class SensorTest { 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 @@ public class SensorTest { 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 e396263b1679..728606f9f628 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.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; +import java.util.Locale; + /** * Unit tests for {@link DeviceStateNotificationController}. * <p/> @@ -77,6 +84,8 @@ public class DeviceStateNotificationControllerTest { 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 @@ public class DeviceStateNotificationControllerTest { 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 @@ public class DeviceStateNotificationControllerTest { 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 @@ public class DeviceStateNotificationControllerTest { 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 aca96ad20385..aad373fdaf95 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 @@ public class SystemConfigTest { + " <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 d50aca94e06b..2efd9fcf5f9c 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 @@ public class VibrationSettingsTest { } @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 @@ public class VibrationSettingsTest { } @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 @@ public class VibrationSettingsTest { } @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 90d148856fff..4406d831dd93 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.service.notification.StatusBarNotification; 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.ByteArrayInputStream; 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 @@ public class NotificationListenersTest extends UiServiceTestCase { .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 9cfdaa7cad0c..dd9f3cb3d343 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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 4e001fe06fb8..37c4b3787835 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.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMor 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 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { 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 8f2b470908c4..0033e3e368c6 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 @@ public class ActivityRecordTests extends WindowTestsBase { 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 7d16fb2e0255..4890f3e6cbf1 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.ActivityManagerInternal; 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 @@ public class ActivityStartInterceptorTest { } @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 @@ public class ActivityStartInterceptorTest { } @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 17ae215c2930..6d7f2c13197c 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 @@ public class BackNavigationControllerTests extends WindowTestsBase { 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 @@ public class BackNavigationControllerTests extends WindowTestsBase { 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 @@ public class BackNavigationControllerTests extends WindowTestsBase { 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 2686a2429492..d2f0385131d5 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 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { } @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 10540dc5a9ee..1ad04a254f66 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 @@ public class DisplayAreaTest extends WindowTestsBase { } @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 206554019526..a8fc25fc4477 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 @@ public class InsetsPolicyTest extends WindowTestsBase { } @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/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index a15ee694d6a4..03c93e976059 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -39,9 +39,9 @@ import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTA import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH; import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; -import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -207,7 +207,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { throws Exception { doReturn(true).when(mLetterboxConfiguration) .isPolicyForIgnoringRequestedOrientationEnabled(); - mockThatProperty(PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED, + mockThatProperty(PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED, /* value */ false); doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); 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 49d8da1b2880..9d597b11120d 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 @@ public class TaskFragmentTest extends WindowTestsBase { // 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 766e74c85dab..460a603d51a2 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.ACTIVITY_TYPE_STANDARD; 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.content.res.Configuration; 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 @@ public class WindowStateTests extends WindowTestsBase { 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 025869dd2999..3158ad8fc58e 100644 --- a/telephony/OWNERS +++ b/telephony/OWNERS @@ -11,12 +11,6 @@ chinmayd@google.com 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 905a90c11957..caafce2f1a7e 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -331,8 +331,8 @@ public final class TelephonyPermissions { * 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 559faf9b20de..64e43568e4d6 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -91,8 +91,7 @@ import java.util.function.Consumer; 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 class SubscriptionManager { 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 @@ public class SubscriptionManager { @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 @@ public class SubscriptionManager { } } - 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 @@ public class SubscriptionManager { 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 @@ public class SubscriptionManager { + " 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 @@ public class SubscriptionManager { + " 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 @@ public class SubscriptionManager { } // 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 @@ public class SubscriptionManager { 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 @@ public class SubscriptionManager { * 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 @@ public class SubscriptionManager { 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 @@ public class SubscriptionManager { * @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 @@ public class SubscriptionManager { * @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 @@ public class SubscriptionManager { * @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 @@ public class SubscriptionManager { * @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 @@ public class SubscriptionManager { } /** @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 @@ public class SubscriptionManager { sGetSlotIndexCache.disableLocal(); sGetSubIdCache.disableLocal(); sGetPhoneIdCache.disableLocal(); - - sIsSubscriptionManagerServiceEnabled.disableLocal(); } /** @@ -4005,14 +3875,6 @@ public class SubscriptionManager { * * @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 @@ public class SubscriptionManager { 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 000000000000..4f1a13623c3b --- /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 000000000000..f4514a6de413 --- /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 6a5380ddb36e..21a6b447d6a4 100644 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -129,9 +129,9 @@ interface ISub { * @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 @@ interface ISub { 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 @@ interface ISub { */ 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 ee9d6c1a2448..282b64d3f43e 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2991,6 +2991,15 @@ interface ITelephony { 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/ActivityManagerPerfTests/utils/Android.bp b/tests/ActivityManagerPerfTests/utils/Android.bp index 99c43c8d8fca..5902c1cbda15 100644 --- a/tests/ActivityManagerPerfTests/utils/Android.bp +++ b/tests/ActivityManagerPerfTests/utils/Android.bp @@ -32,6 +32,6 @@ java_test { static_libs: [ "androidx.test.rules", "junit", - "ub-uiautomator", + "androidx.test.uiautomator_uiautomator", ], } diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java index fc787bafa93a..9bd94f2a9a1e 100644 --- a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java +++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java @@ -19,10 +19,10 @@ package com.android.frameworks.perftests.am.util; import android.content.Intent; import android.os.RemoteException; import android.os.ResultReceiver; -import android.support.test.uiautomator.UiDevice; import android.util.Log; import androidx.test.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; import java.io.IOException; diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt index 8f07f213521b..0b09e248b221 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt @@ -117,6 +117,11 @@ class OpenAppFromLockNotificationWithLockOverlayApp(flicker: FlickerTest) : @Test override fun entireScreenCovered() = super.entireScreenCovered() + @FlakyTest(bugId = 278227468) + @Test + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() + companion object { /** * Creates the test configurations. 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 18e49fe1078f..ae9ca8007dc8 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 @@ class OpenCameraOnDoubleClickPowerButton(flicker: FlickerTest) : @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 @@ class OpenCameraOnDoubleClickPowerButton(flicker: FlickerTest) : @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 @@ class OpenCameraOnDoubleClickPowerButton(flicker: FlickerTest) : 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/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt index 6fa65fd940ec..be735477dc40 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt @@ -118,7 +118,7 @@ class TaskTransitionTest(flicker: FlickerTest) : BaseTest(flicker) { } /** Checks that a color background is visible while the task transition is occurring. */ - @Presubmit + @FlakyTest(bugId = 265007895) @Test fun transitionHasColorBackground() { val backgroundColorLayer = ComponentNameMatcher("", "Animation Background") diff --git a/tests/InputMethodStressTest/Android.bp b/tests/InputMethodStressTest/Android.bp index 0ad38768238a..27640a5f51bc 100644 --- a/tests/InputMethodStressTest/Android.bp +++ b/tests/InputMethodStressTest/Android.bp @@ -32,5 +32,8 @@ android_test { "general-tests", "vts", ], - sdk_version: "31", + data: [ + ":SimpleTestIme", + ], + sdk_version: "current", } diff --git a/tests/InputMethodStressTest/AndroidManifest.xml b/tests/InputMethodStressTest/AndroidManifest.xml index 2d183bcb81fd..62eee0270cac 100644 --- a/tests/InputMethodStressTest/AndroidManifest.xml +++ b/tests/InputMethodStressTest/AndroidManifest.xml @@ -17,7 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.inputmethod.stresstest"> - + <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <application> <activity android:name=".ImeStressTestUtil$TestActivity" android:configChanges="orientation|screenSize"/> diff --git a/tests/InputMethodStressTest/AndroidTest.xml b/tests/InputMethodStressTest/AndroidTest.xml index 9ac41351f684..bedf0990a188 100644 --- a/tests/InputMethodStressTest/AndroidTest.xml +++ b/tests/InputMethodStressTest/AndroidTest.xml @@ -25,6 +25,7 @@ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="SimpleTestIme.apk" /> <option name="test-file-name" value="InputMethodStressTest.apk" /> </target_preparer> diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java index 9c70e6e568c4..3d257b29287f 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java @@ -61,14 +61,10 @@ import java.util.List; @RunWith(Parameterized.class) public final class AutoShowTest { - @Rule(order = 0) public DisableLockScreenRule mDisableLockScreenRule = - new DisableLockScreenRule(); - @Rule(order = 1) public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule(); - @Rule(order = 2) public ScreenOrientationRule mScreenOrientationRule = - new ScreenOrientationRule(true /* isPortrait */); - @Rule(order = 3) public PressHomeBeforeTestRule mPressHomeBeforeTestRule = - new PressHomeBeforeTestRule(); - @Rule(order = 4) public ScreenCaptureRule mScreenCaptureRule = + @Rule(order = 0) public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule(); + @Rule(order = 1) public ImeStressTestRule mImeStressTestRule = + new ImeStressTestRule(true /* useSimpleTestIme */); + @Rule(order = 2) public ScreenCaptureRule mScreenCaptureRule = new ScreenCaptureRule("/sdcard/InputMethodStressTest"); @Parameterized.Parameters( name = "windowFocusFlags={0}, softInputVisibility={1}, softInputAdjustment={2}") diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DisableLockScreenRule.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DisableLockScreenRule.java deleted file mode 100644 index d95decff2d86..000000000000 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DisableLockScreenRule.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.stresstest; - -import android.support.test.uiautomator.UiDevice; - -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; - -import java.io.IOException; - -/** Disable lock screen during the test. */ -public class DisableLockScreenRule extends TestWatcher { - private static final String LOCK_SCREEN_OFF_COMMAND = "locksettings set-disabled true"; - private static final String LOCK_SCREEN_ON_COMMAND = "locksettings set-disabled false"; - - private final UiDevice mUiDevice = - UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - - @Override - protected void starting(Description description) { - try { - mUiDevice.executeShellCommand(LOCK_SCREEN_OFF_COMMAND); - } catch (IOException e) { - throw new RuntimeException("Could not disable lock screen.", e); - } - } - - @Override - protected void finished(Description description) { - try { - mUiDevice.executeShellCommand(LOCK_SCREEN_ON_COMMAND); - } catch (IOException e) { - throw new RuntimeException("Could not enable lock screen.", e); - } - } -} diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java index 9d4aefb69386..7632ab08b655 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java @@ -68,14 +68,10 @@ public final class ImeOpenCloseStressTest { private static final String TAG = "ImeOpenCloseStressTest"; private static final int NUM_TEST_ITERATIONS = 10; - @Rule(order = 0) public DisableLockScreenRule mDisableLockScreenRule = - new DisableLockScreenRule(); - @Rule(order = 1) public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule(); - @Rule(order = 2) public ScreenOrientationRule mScreenOrientationRule = - new ScreenOrientationRule(true /* isPortrait */); - @Rule(order = 3) public PressHomeBeforeTestRule mPressHomeBeforeTestRule = - new PressHomeBeforeTestRule(); - @Rule(order = 4) public ScreenCaptureRule mScreenCaptureRule = + @Rule(order = 0) public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule(); + @Rule(order = 1) public ImeStressTestRule mImeStressTestRule = + new ImeStressTestRule(true /* useSimpleTestIme */); + @Rule(order = 2) public ScreenCaptureRule mScreenCaptureRule = new ScreenCaptureRule("/sdcard/InputMethodStressTest"); private final Instrumentation mInstrumentation; @@ -499,8 +495,6 @@ public final class ImeOpenCloseStressTest { @Test public void testRotateScreenWithKeyboardOn() throws Exception { - // TODO(b/256739702): Keyboard disappears after rotating screen to landscape mode if - // android:configChanges="orientation|screenSize" is not set Intent intent = createIntent( mWindowFocusFlags, diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java new file mode 100644 index 000000000000..12104b298dac --- /dev/null +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java @@ -0,0 +1,153 @@ +/* + * 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.inputmethod.stresstest; + +import android.app.Instrumentation; +import android.os.RemoteException; +import android.support.test.uiautomator.UiDevice; + +import androidx.test.platform.app.InstrumentationRegistry; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +import java.io.IOException; + +/** + * Do setup and cleanup for Ime stress tests, including disabling lock and auto-rotate screen, + * pressing home and enabling a simple test Ime during the tests. + */ +public class ImeStressTestRule extends TestWatcher { + private static final String LOCK_SCREEN_OFF_COMMAND = "locksettings set-disabled true"; + private static final String LOCK_SCREEN_ON_COMMAND = "locksettings set-disabled false"; + private static final String SET_PORTRAIT_MODE_COMMAND = "settings put system user_rotation 0"; + private static final String SET_LANDSCAPE_MODE_COMMAND = "settings put system user_rotation 1"; + private static final String SIMPLE_IME_ID = + "com.android.apps.inputmethod.simpleime/.SimpleInputMethodService"; + private static final String ENABLE_IME_COMMAND = "ime enable " + SIMPLE_IME_ID; + private static final String SET_IME_COMMAND = "ime set " + SIMPLE_IME_ID; + private static final String DISABLE_IME_COMMAND = "ime disable " + SIMPLE_IME_ID; + private static final String RESET_IME_COMMAND = "ime reset"; + + @NonNull private final Instrumentation mInstrumentation; + @NonNull private final UiDevice mUiDevice; + // Whether the screen orientation is set to portrait. + private boolean mIsPortrait; + // Whether to use a simple test Ime or system default Ime for test. + private final boolean mUseSimpleTestIme; + + public ImeStressTestRule(boolean useSimpleTestIme) { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mUiDevice = UiDevice.getInstance(mInstrumentation); + // Default is portrait mode + mIsPortrait = true; + mUseSimpleTestIme = useSimpleTestIme; + } + + public void setIsPortrait(boolean isPortrait) { + mIsPortrait = isPortrait; + } + + @Override + protected void starting(Description description) { + disableLockScreen(); + setOrientation(); + mUiDevice.pressHome(); + if (mUseSimpleTestIme) { + enableSimpleIme(); + } else { + resetImeToDefault(); + } + + mInstrumentation.waitForIdleSync(); + } + + @Override + protected void finished(Description description) { + if (mUseSimpleTestIme) { + disableSimpleIme(); + } + unfreezeRotation(); + restoreLockScreen(); + } + + private void disableLockScreen() { + try { + executeShellCommand(LOCK_SCREEN_OFF_COMMAND); + } catch (IOException e) { + throw new RuntimeException("Could not disable lock screen.", e); + } + } + + private void restoreLockScreen() { + try { + executeShellCommand(LOCK_SCREEN_ON_COMMAND); + } catch (IOException e) { + throw new RuntimeException("Could not enable lock screen.", e); + } + } + + private void setOrientation() { + try { + mUiDevice.freezeRotation(); + executeShellCommand( + mIsPortrait ? SET_PORTRAIT_MODE_COMMAND : SET_LANDSCAPE_MODE_COMMAND); + } catch (IOException e) { + throw new RuntimeException("Could not set screen orientation.", e); + } catch (RemoteException e) { + throw new RuntimeException("Could not freeze rotation.", e); + } + } + + private void unfreezeRotation() { + try { + mUiDevice.unfreezeRotation(); + } catch (RemoteException e) { + throw new RuntimeException("Could not unfreeze screen rotation.", e); + } + } + + private void enableSimpleIme() { + try { + executeShellCommand(ENABLE_IME_COMMAND); + executeShellCommand(SET_IME_COMMAND); + } catch (IOException e) { + throw new RuntimeException("Could not enable SimpleTestIme.", e); + } + } + + private void disableSimpleIme() { + try { + executeShellCommand(DISABLE_IME_COMMAND); + } catch (IOException e) { + throw new RuntimeException("Could not disable SimpleTestIme.", e); + } + } + + private void resetImeToDefault() { + try { + executeShellCommand(RESET_IME_COMMAND); + } catch (IOException e) { + throw new RuntimeException("Could not reset Ime to default.", e); + } + } + + private @NonNull String executeShellCommand(@NonNull String cmd) throws IOException { + return mUiDevice.executeShellCommand(cmd); + } +} diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java index d2708ad47712..f4a04a163ebb 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java @@ -77,11 +77,10 @@ public final class NotificationTest { private static final BySelector REPLY_SEND_BUTTON_SELECTOR = By.res("com.android.systemui", "remote_input_send").enabled(true); - @Rule - public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule(); - - @Rule - public ScreenCaptureRule mScreenCaptureRule = + @Rule(order = 0) public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule(); + @Rule(order = 1) public ImeStressTestRule mImeStressTestRule = + new ImeStressTestRule(true /* useSimpleTestIme */); + @Rule(order = 2) public ScreenCaptureRule mScreenCaptureRule = new ScreenCaptureRule("/sdcard/InputMethodStressTest"); private Context mContext; @@ -141,7 +140,8 @@ public final class NotificationTest { // Post inline reply notification. PendingIntent pendingIntent = PendingIntent.getBroadcast( - mContext, REPLY_REQUEST_CODE, new Intent().setAction(ACTION_REPLY), + mContext, REPLY_REQUEST_CODE, + new Intent().setAction(ACTION_REPLY).setClass(mContext, NotificationTest.class), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); RemoteInput remoteInput = new RemoteInput.Builder(REPLY_INPUT_KEY) .setLabel(REPLY_INPUT_LABEL) diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ScreenOrientationRule.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ScreenOrientationRule.java deleted file mode 100644 index bc3b1efcf6b5..000000000000 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ScreenOrientationRule.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.stresstest; - -import android.os.RemoteException; -import android.support.test.uiautomator.UiDevice; - -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; - -import java.io.IOException; - -/** - * Disable auto-rotate during the test and set the screen orientation to portrait or landscape - * before the test starts. - */ -public class ScreenOrientationRule extends TestWatcher { - private static final String SET_PORTRAIT_MODE_CMD = "settings put system user_rotation 0"; - private static final String SET_LANDSCAPE_MODE_CMD = "settings put system user_rotation 1"; - - private final boolean mIsPortrait; - private final UiDevice mUiDevice = - UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - - ScreenOrientationRule(boolean isPortrait) { - mIsPortrait = isPortrait; - } - - @Override - protected void starting(Description description) { - try { - mUiDevice.freezeRotation(); - mUiDevice.executeShellCommand(mIsPortrait ? SET_PORTRAIT_MODE_CMD : - SET_LANDSCAPE_MODE_CMD); - } catch (IOException e) { - throw new RuntimeException("Could not set screen orientation.", e); - } catch (RemoteException e) { - throw new RuntimeException("Could not freeze rotation.", e); - } - } - - @Override - protected void finished(Description description) { - try { - mUiDevice.unfreezeRotation(); - } catch (RemoteException e) { - throw new RuntimeException("Could not unfreeze screen rotation.", e); - } - } -} diff --git a/tests/SharedLibraryLoadingTest/AndroidTest.xml b/tests/SharedLibraryLoadingTest/AndroidTest.xml index 947453d07bd9..ad0584724fdc 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/tests/SilkFX/assets/gainmaps/city_night.jpg b/tests/SilkFX/assets/gainmaps/city_night.jpg Binary files differindex cdb4311ddcbe..ba26ed6a5780 100644 --- a/tests/SilkFX/assets/gainmaps/city_night.jpg +++ b/tests/SilkFX/assets/gainmaps/city_night.jpg diff --git a/tests/SilkFX/assets/gainmaps/desert_palms.jpg b/tests/SilkFX/assets/gainmaps/desert_palms.jpg Binary files differindex c337aadc2d4d..048178670a96 100644 --- a/tests/SilkFX/assets/gainmaps/desert_palms.jpg +++ b/tests/SilkFX/assets/gainmaps/desert_palms.jpg diff --git a/tests/SilkFX/assets/gainmaps/desert_sunset.jpg b/tests/SilkFX/assets/gainmaps/desert_sunset.jpg Binary files differindex fa15f5606af6..919a1574a4b9 100644 --- a/tests/SilkFX/assets/gainmaps/desert_sunset.jpg +++ b/tests/SilkFX/assets/gainmaps/desert_sunset.jpg diff --git a/tests/SilkFX/assets/gainmaps/desert_wanda.jpg b/tests/SilkFX/assets/gainmaps/desert_wanda.jpg Binary files differindex 33f69a92bac3..f5a2ef9c53ea 100644 --- a/tests/SilkFX/assets/gainmaps/desert_wanda.jpg +++ b/tests/SilkFX/assets/gainmaps/desert_wanda.jpg diff --git a/tests/SilkFX/assets/gainmaps/fountain_night.jpg b/tests/SilkFX/assets/gainmaps/fountain_night.jpg Binary files differindex 863127b7ac59..d8b2d759e4c0 100644 --- a/tests/SilkFX/assets/gainmaps/fountain_night.jpg +++ b/tests/SilkFX/assets/gainmaps/fountain_night.jpg diff --git a/tests/SilkFX/assets/gainmaps/grand_canyon.jpg b/tests/SilkFX/assets/gainmaps/grand_canyon.jpg Binary files differindex 12cd9665776e..2f605bbb0a7e 100644 --- a/tests/SilkFX/assets/gainmaps/grand_canyon.jpg +++ b/tests/SilkFX/assets/gainmaps/grand_canyon.jpg diff --git a/tests/SilkFX/assets/gainmaps/lamps.jpg b/tests/SilkFX/assets/gainmaps/lamps.jpg Binary files differindex 65bda89ad660..768665f643cb 100644 --- a/tests/SilkFX/assets/gainmaps/lamps.jpg +++ b/tests/SilkFX/assets/gainmaps/lamps.jpg diff --git a/tests/SilkFX/assets/gainmaps/mountain_lake.jpg b/tests/SilkFX/assets/gainmaps/mountain_lake.jpg Binary files differindex b2b10d287c69..b7981fdca6da 100644 --- a/tests/SilkFX/assets/gainmaps/mountain_lake.jpg +++ b/tests/SilkFX/assets/gainmaps/mountain_lake.jpg diff --git a/tests/SilkFX/assets/gainmaps/mountains.jpg b/tests/SilkFX/assets/gainmaps/mountains.jpg Binary files differindex 82acd45fc0d8..fe69993e0706 100644 --- a/tests/SilkFX/assets/gainmaps/mountains.jpg +++ b/tests/SilkFX/assets/gainmaps/mountains.jpg diff --git a/tests/SilkFX/assets/gainmaps/sunflower.jpg b/tests/SilkFX/assets/gainmaps/sunflower.jpg Binary files differindex 55b1b14c0ff4..4b17614d66bf 100644 --- a/tests/SilkFX/assets/gainmaps/sunflower.jpg +++ b/tests/SilkFX/assets/gainmaps/sunflower.jpg diff --git a/tests/SilkFX/assets/gainmaps/train_station_night.jpg b/tests/SilkFX/assets/gainmaps/train_station_night.jpg Binary files differindex 45142bb326ee..ecd45ee1e629 100644 --- a/tests/SilkFX/assets/gainmaps/train_station_night.jpg +++ b/tests/SilkFX/assets/gainmaps/train_station_night.jpg diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp index 25373f9e9e2f..554f64e73b74 100644 --- a/tools/validatekeymaps/Android.bp +++ b/tools/validatekeymaps/Android.bp @@ -15,7 +15,7 @@ package { cc_binary_host { name: "validatekeymaps", - + cpp_std: "c++20", srcs: ["Main.cpp"], cflags: [ 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 166fbddc2f56..c6e675ade262 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 final class NetworkProviderInfo implements Parcelable { 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; |