Merge "Allow UI update upon auth/action entry changes."
diff --git a/PERFORMANCE_OWNERS b/PERFORMANCE_OWNERS
new file mode 100644
index 0000000..9452ea3
--- /dev/null
+++ b/PERFORMANCE_OWNERS
@@ -0,0 +1,5 @@
+timmurray@google.com
+edgararriaga@google.com
+dualli@google.com
+carmenjackson@google.com
+philipcuadra@google.com
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index b87dec1..f18b11e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -434,7 +434,7 @@
final UidStats uidStats =
getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
- if (jobStatus.shouldTreatAsExpeditedJob() && jobStatus.shouldTreatAsUserInitiatedJob()) {
+ if (jobStatus.shouldTreatAsExpeditedJob() || jobStatus.shouldTreatAsUserInitiatedJob()) {
if (!jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY)) {
// Don't request a direct hole through any of the firewalls. Instead, mark the
// constraint as satisfied if the network is available, and the job will get
@@ -444,7 +444,9 @@
}
// Don't need to update constraint here if the network goes away. We'll do that as part
// of regular processing when we're notified about the drop.
- } else if (jobStatus.isRequestedExpeditedJob()
+ } else if (((jobStatus.isRequestedExpeditedJob() && !jobStatus.shouldTreatAsExpeditedJob())
+ || (jobStatus.getJob().isUserInitiated()
+ && !jobStatus.shouldTreatAsUserInitiatedJob()))
&& jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY)) {
// Make sure we don't accidentally keep the constraint as satisfied if the job went
// from being expedited-ready to not-expeditable.
diff --git a/core/api/current.txt b/core/api/current.txt
index df10ffe..381b387 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6717,6 +6717,7 @@
method public boolean areNotificationsEnabled();
method public boolean areNotificationsPaused();
method public boolean canNotifyAsPackage(@NonNull String);
+ method public boolean canSendFullScreenIntent();
method public void cancel(int);
method public void cancel(@Nullable String, int);
method public void cancelAll();
@@ -36766,6 +36767,7 @@
field public static final String ACTION_MANAGE_ALL_SIM_PROFILES_SETTINGS = "android.settings.MANAGE_ALL_SIM_PROFILES_SETTINGS";
field public static final String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
field public static final String ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION";
+ field public static final String ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT = "android.settings.MANAGE_APP_USE_FULL_SCREEN_INTENT";
field public static final String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
field public static final String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
field public static final String ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING = "android.settings.MANAGE_SUPERVISOR_RESTRICTED_SETTING";
@@ -40256,7 +40258,7 @@
}
public class BeginGetCredentialOption implements android.os.Parcelable {
- ctor public BeginGetCredentialOption(@NonNull String, @NonNull android.os.Bundle);
+ ctor public BeginGetCredentialOption(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
method public int describeContents();
method @NonNull public android.os.Bundle getCandidateQueryData();
method @NonNull public String getType();
@@ -40339,8 +40341,9 @@
}
public class CredentialEntry implements android.os.Parcelable {
- ctor public CredentialEntry(@NonNull String, @NonNull android.app.slice.Slice);
+ ctor public CredentialEntry(@NonNull android.service.credentials.BeginGetCredentialOption, @NonNull android.app.slice.Slice);
method public int describeContents();
+ method @NonNull public android.service.credentials.BeginGetCredentialOption getBeginGetCredentialOption();
method @NonNull public android.app.slice.Slice getSlice();
method @NonNull public String getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a7c2bf1..fee4001 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3274,6 +3274,7 @@
field public static final String SAFETY_CENTER_SERVICE = "safety_center";
field public static final String SEARCH_UI_SERVICE = "search_ui";
field public static final String SECURE_ELEMENT_SERVICE = "secure_element";
+ field public static final String SHARED_CONNECTIVITY_SERVICE = "shared_connectivity";
field public static final String SMARTSPACE_SERVICE = "smartspace";
field public static final String STATS_MANAGER = "stats";
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
@@ -8916,6 +8917,10 @@
field public static final int FRONTEND_STATUS_READINESS_UNSUPPORTED = 4; // 0x4
}
+ public class IptvFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
+ method public int getProtocolCapability();
+ }
+
public class IptvFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull public static android.media.tv.tuner.frontend.IptvFrontendSettings.Builder builder();
method @IntRange(from=0) public long getBitrate();
@@ -9843,7 +9848,6 @@
}
public class SharedConnectivityManager {
- ctor public SharedConnectivityManager(@NonNull android.content.Context);
method public boolean connectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
method public boolean connectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
method public boolean disconnectTetherNetwork();
@@ -15846,11 +15850,12 @@
}
public final class MediaQualityStatus implements android.os.Parcelable {
+ ctor public MediaQualityStatus(@NonNull String, int, int, @IntRange(from=0, to=100) int, @IntRange(from=0) int, @IntRange(from=0) long);
method public int describeContents();
method @NonNull public String getCallSessionId();
method public int getMediaSessionType();
- method public long getRtpInactivityMillis();
- method public int getRtpJitterMillis();
+ method @IntRange(from=0) public long getRtpInactivityMillis();
+ method @IntRange(from=0) public int getRtpJitterMillis();
method @IntRange(from=0, to=100) public int getRtpPacketLossRate();
method public int getTransportType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -15859,14 +15864,6 @@
field public static final int MEDIA_SESSION_TYPE_VIDEO = 2; // 0x2
}
- public static final class MediaQualityStatus.Builder {
- ctor public MediaQualityStatus.Builder(@NonNull String, int, int);
- method @NonNull public android.telephony.ims.MediaQualityStatus build();
- method @NonNull public android.telephony.ims.MediaQualityStatus.Builder setRtpInactivityMillis(long);
- method @NonNull public android.telephony.ims.MediaQualityStatus.Builder setRtpJitterMillis(int);
- method @NonNull public android.telephony.ims.MediaQualityStatus.Builder setRtpPacketLossRate(@IntRange(from=0, to=100) int);
- }
-
public final class MediaThreshold implements android.os.Parcelable {
method public int describeContents();
method @NonNull public long[] getThresholdsRtpInactivityTimeMillis();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 6a4584b..85861c3 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1735,6 +1735,7 @@
public final class MediaCas implements java.lang.AutoCloseable {
method public void forceResourceLost();
+ method public boolean isAidlHal();
}
public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint {
@@ -1745,6 +1746,10 @@
method public int getMaxMacroBlocks();
}
+ public final class MediaDescrambler implements java.lang.AutoCloseable {
+ method public boolean isAidlHal();
+ }
+
public final class MediaRoute2Info implements android.os.Parcelable {
method @NonNull public String getOriginalId();
}
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index e93ce6b..c628ec4 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -734,7 +734,22 @@
* guarantees that the format is stable across devices or Android releases.</p>
*/
public @Nullable String getDescription() {
- return mDescription;
+ final StringBuilder sb = new StringBuilder();
+
+ if (mSubReason != SUBREASON_UNKNOWN) {
+ sb.append("[");
+ sb.append(subreasonToString(mSubReason));
+ sb.append("]");
+ }
+
+ if (!TextUtils.isEmpty(mDescription)) {
+ if (sb.length() > 0) {
+ sb.append(" ");
+ }
+ sb.append(mDescription);
+ }
+
+ return sb.toString();
}
/**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index f6d27ad..4e94a1d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -31,6 +31,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.PermissionChecker;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
@@ -855,6 +856,32 @@
}
/**
+ * Returns whether the calling app can send fullscreen intents.
+ * <p>From Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, apps may not have
+ * permission to use {@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}. If permission
+ * is denied, notification will show up as an expanded heads up notification on lockscreen.
+ * <p> To request access, add the {@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}
+ * permission to your manifest, and use
+ * {@link android.provider.Settings#ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT}.
+ */
+ public boolean canSendFullScreenIntent() {
+ final int result = PermissionChecker.checkPermissionForPreflight(mContext,
+ android.Manifest.permission.USE_FULL_SCREEN_INTENT,
+ mContext.getAttributionSource());
+
+ switch (result) {
+ case PermissionChecker.PERMISSION_GRANTED:
+ return true;
+ case PermissionChecker.PERMISSION_SOFT_DENIED:
+ case PermissionChecker.PERMISSION_HARD_DENIED:
+ return false;
+ default:
+ if (localLOGV) Log.v(TAG, "Unknown PermissionChecker result: " + result);
+ return false;
+ }
+ }
+
+ /**
* Creates a group container for {@link NotificationChannel} objects.
*
* This can be used to rename an existing group.
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 031d351..0365f8c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -156,6 +156,7 @@
import android.net.vcn.VcnManager;
import android.net.wifi.WifiFrameworkInitializer;
import android.net.wifi.nl80211.WifiNl80211Manager;
+import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager;
import android.nfc.NfcManager;
import android.ondevicepersonalization.OnDevicePersonalizationFrameworkInitializer;
import android.os.BatteryManager;
@@ -1572,6 +1573,13 @@
Context.GRAMMATICAL_INFLECTION_SERVICE)));
}});
+ registerService(Context.SHARED_CONNECTIVITY_SERVICE, SharedConnectivityManager.class,
+ new CachedServiceFetcher<SharedConnectivityManager>() {
+ @Override
+ public SharedConnectivityManager createService(ContextImpl ctx) {
+ return new SharedConnectivityManager(ctx);
+ }
+ });
sInitializing = true;
try {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6aa7f3f..0d330ce 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6232,6 +6232,18 @@
public static final String SATELLITE_SERVICE = "satellite";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.net.wifi.sharedconnectivity.app.SharedConnectivityManager} for accessing
+ * shared connectivity services.
+ *
+ * @see #getSystemService(String)
+ * @see android.net.wifi.sharedconnectivity.app.SharedConnectivityManager
+ * @hide
+ */
+ @SystemApi
+ public static final String SHARED_CONNECTIVITY_SERVICE = "shared_connectivity";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fce9f4c..3060b7f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -10952,10 +10952,13 @@
/**
* Attempt to relinquish the update ownership of the given package. Only the current
- * update owner of the given package can use this API or a SecurityException will be
- * thrown.
+ * update owner of the given package can use this API.
*
* @param targetPackage The installed package whose update owner will be changed.
+ * @throws IllegalArgumentException if the given package is invalid.
+ * @throws SecurityException if you are not the current update owner of the given package.
+ *
+ * @see PackageInstaller.SessionParams#setRequestUpdateOwnership
*/
public void relinquishUpdateOwnership(@NonNull String targetPackage) {
throw new UnsupportedOperationException(
diff --git a/core/java/android/credentials/CredentialDescription.java b/core/java/android/credentials/CredentialDescription.java
index c67d37e..f9db5e8 100644
--- a/core/java/android/credentials/CredentialDescription.java
+++ b/core/java/android/credentials/CredentialDescription.java
@@ -48,7 +48,7 @@
private final String mFlattenedRequestString;
/**
- * The entry to be used in the UI.
+ * The credential entries to be used in the UI.
*/
@NonNull
private final List<CredentialEntry> mCredentialEntries;
@@ -128,16 +128,25 @@
dest.writeTypedList(mCredentialEntries, flags);
}
+ /**
+ * Returns the type of the Credential described.
+ */
@NonNull
public String getType() {
return mType;
}
+ /**
+ * Returns the flattened JSON string that will be matched with requests.
+ */
@NonNull
public String getFlattenedRequestString() {
return mFlattenedRequestString;
}
+ /**
+ * Returns the credential entries to be used in the UI.
+ */
@NonNull
public List<CredentialEntry> getCredentialEntries() {
return mCredentialEntries;
@@ -151,6 +160,7 @@
@Override
public boolean equals(Object obj) {
return Objects.equals(mType, ((CredentialDescription) obj).getType())
- && Objects.equals(mFlattenedRequestString, ((CredentialDescription) obj).getType());
+ && Objects.equals(mFlattenedRequestString, ((CredentialDescription) obj)
+ .getFlattenedRequestString());
}
}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index c9fc722..e9df553 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -124,7 +124,7 @@
*
* <p>Note that if 2 surfaces share the same stream via {@link
* OutputConfiguration#enableSurfaceSharing} and {@link OutputConfiguration#addSurface},
- * prepare() only needs to be called on one surface, and {link
+ * prepare() only needs to be called on one surface, and {@link
* StateCallback#onSurfacePrepared} will be triggered for both surfaces.</p>
*
* <p>{@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index dcf0026..6f9c9dd 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -22,6 +22,7 @@
import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -37,6 +38,10 @@
import com.android.internal.util.Preconditions;
import com.android.server.vcn.util.PersistableBundleUtils;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -54,6 +59,17 @@
public final class VcnConfig implements Parcelable {
@NonNull private static final String TAG = VcnConfig.class.getSimpleName();
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"TRANSPORT_"},
+ value = {
+ NetworkCapabilities.TRANSPORT_CELLULAR,
+ NetworkCapabilities.TRANSPORT_WIFI,
+ })
+ @Target({ElementType.TYPE_USE})
+ public @interface VcnUnderlyingNetworkTransport {}
+
private static final Set<Integer> ALLOWED_TRANSPORTS = new ArraySet<>();
static {
@@ -164,7 +180,7 @@
* @see Builder#setRestrictedUnderlyingNetworkTransports(Set)
*/
@NonNull
- public Set<Integer> getRestrictedUnderlyingNetworkTransports() {
+ public Set<@VcnUnderlyingNetworkTransport Integer> getRestrictedUnderlyingNetworkTransports() {
return Collections.unmodifiableSet(mRestrictedTransports);
}
@@ -308,16 +324,20 @@
/**
* Sets transports that will be restricted by the VCN.
*
- * @param transports transports that will be restricted by VCN. Networks that include any
- * of the transports will be marked as restricted. Only {@link
- * NetworkCapabilities#TRANSPORT_WIFI} and {@link
- * NetworkCapabilities#TRANSPORT_CELLULAR} are allowed. {@link
+ * <p>In general, apps will not be able to bind to, or use a restricted network. In other
+ * words, unless the network type is marked restricted, any app can opt to use underlying
+ * networks, instead of through the VCN.
+ *
+ * @param transports transports that will be restricted by VCN. Networks that include any of
+ * the transports will be marked as restricted. {@link
* NetworkCapabilities#TRANSPORT_WIFI} is marked restricted by default.
* @return this {@link Builder} instance, for chaining
* @throws IllegalArgumentException if the input contains unsupported transport types.
+ * @see NetworkCapabilities#NET_CAPABILITY_NOT_RESTRICTED
*/
@NonNull
- public Builder setRestrictedUnderlyingNetworkTransports(@NonNull Set<Integer> transports) {
+ public Builder setRestrictedUnderlyingNetworkTransports(
+ @NonNull Set<@VcnUnderlyingNetworkTransport Integer> transports) {
validateRestrictedTransportsOrThrow(transports);
mRestrictedTransports.clear();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6829cd7..cf68572 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -19368,6 +19368,18 @@
"android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION";
/**
+ * Activity Action: Show screen for controlling whether an app can send full screen intents.
+ * <p>
+ * Input: the intent's data URI must specify the application package name for which you want
+ * to manage full screen intents.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT =
+ "android.settings.MANAGE_APP_USE_FULL_SCREEN_INTENT";
+
+ /**
* Activity Action: For system or preinstalled apps to show their {@link Activity} embedded
* in Settings app on large screen devices.
*
diff --git a/core/java/android/service/credentials/BeginGetCredentialOption.java b/core/java/android/service/credentials/BeginGetCredentialOption.java
index 65d63c3..1df908a 100644
--- a/core/java/android/service/credentials/BeginGetCredentialOption.java
+++ b/core/java/android/service/credentials/BeginGetCredentialOption.java
@@ -39,6 +39,11 @@
*/
@SuppressLint("ParcelNotFinal")
public class BeginGetCredentialOption implements Parcelable {
+ /**
+ * A unique id associated with this request option.
+ */
+ @NonNull
+ private final String mId;
/**
* The requested credential type.
@@ -53,6 +58,18 @@
private final Bundle mCandidateQueryData;
/**
+ * Returns the unique id associated with this request. Providers must pass this id
+ * to the constructor of {@link CredentialEntry} while creating a candidate credential
+ * entry for this request option.
+ *
+ * @hide
+ */
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
+ /**
* Returns the requested credential type.
*/
@NonNull
@@ -80,6 +97,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mType);
dest.writeBundle(mCandidateQueryData);
+ dest.writeString8(mId);
}
@Override
@@ -92,20 +110,22 @@
return "GetCredentialOption {"
+ "type=" + mType
+ ", candidateQueryData=" + mCandidateQueryData
+ + ", id=" + mId
+ "}";
}
/**
* Constructs a {@link BeginGetCredentialOption}.
*
- * @param type the requested credential type
+ * @param id the unique id associated with this option
+ * @param type the requested credential type
* @param candidateQueryData the request candidateQueryData
- *
* @throws IllegalArgumentException If type is empty.
*/
public BeginGetCredentialOption(
- @NonNull String type,
+ @NonNull String id, @NonNull String type,
@NonNull Bundle candidateQueryData) {
+ mId = id;
mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
mCandidateQueryData = requireNonNull(
candidateQueryData, "candidateQueryData must not be null");
@@ -114,11 +134,13 @@
private BeginGetCredentialOption(@NonNull Parcel in) {
String type = in.readString8();
Bundle candidateQueryData = in.readBundle();
+ String id = in.readString8();
mType = type;
AnnotationValidations.validate(NonNull.class, null, mType);
mCandidateQueryData = candidateQueryData;
AnnotationValidations.validate(NonNull.class, null, mCandidateQueryData);
+ mId = id;
}
public static final @NonNull Creator<BeginGetCredentialOption> CREATOR =
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
index b037268..b6c13c4 100644
--- a/core/java/android/service/credentials/CredentialEntry.java
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -16,7 +16,10 @@
package android.service.credentials;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.app.slice.Slice;
@@ -29,9 +32,11 @@
* user.
*
* <p>If user selects this entry, the corresponding {@link PendingIntent},
- * set on the {@code slice} as a {@link androidx.slice.core.SliceAction} will be
- * invoked to launch activities that require some user engagement before getting
- * the credential corresponding to this entry, e.g. authentication, confirmation etc.
+ * set on the {@code slice} will be invoked to launch activities that require some user engagement
+ * before getting the credential corresponding to this entry, e.g. authentication,
+ * confirmation etc. The extras associated with the resulting {@link android.app.Activity} will
+ * also contain the complete credential request containing all required parameters. This request
+ * can be retrieved against {@link CredentialProviderService#EXTRA_GET_CREDENTIAL_REQUEST}.
*
* Once the activity fulfills the required user engagement, the {@link android.app.Activity}
* result should be set to {@link android.app.Activity#RESULT_OK}, and the
@@ -42,24 +47,69 @@
* object passed into the constructor. Any other field will not be parceled through. If the
* derived class has custom parceling implementation, this class will not be able to unpack
* the parcel without having access to that implementation.
+ *
+ * <p>While creating this entry, providers must set a {@code requestId} to be retrieved
+ * from {@link BeginGetCredentialOption#getId()}, to determine for which request this entry is
+ * being presented to the user. This will ensure that when user selects the entry, the correct
+ * complete request is added to the {@link PendingIntent} mentioned above.
*/
@SuppressLint("ParcelNotFinal")
public class CredentialEntry implements Parcelable {
+ /** The request option that corresponds to this entry. **/
+ private final @Nullable BeginGetCredentialOption mBeginGetCredentialOption;
+
/** The type of the credential entry to be shown on the UI. */
private final @NonNull String mType;
+
/** The object containing display content to be shown along with this credential entry
* on the UI. */
private final @NonNull Slice mSlice;
+ /**
+ * Creates an entry that is associated with a {@link BeginGetCredentialOption} request.
+ * Providers must use this constructor when they extend from {@link CredentialProviderService}
+ * to respond to query phase {@link CredentialProviderService#onBeginGetCredential}
+ * credential retrieval requests.
+ *
+ * @param beginGetCredentialOption the request option for which this credential entry is
+ * being constructed This helps maintain an association,
+ * such that when the user selects this entry, providers
+ * can receive the conmplete corresponding request.
+ * @param slice the slice containing the metadata to be shown on the UI. Must be
+ * constructed through the androidx.credentials jetpack library.
+ */
+ public CredentialEntry(@NonNull BeginGetCredentialOption beginGetCredentialOption,
+ @NonNull Slice slice) {
+ mBeginGetCredentialOption = requireNonNull(beginGetCredentialOption,
+ "beginGetCredentialOption must not be null");
+ mType = requireNonNull(mBeginGetCredentialOption.getType(),
+ "type must not be null");
+ mSlice = requireNonNull(slice, "slice must not be null");
+ }
+
+ /**
+ * Creates an entry that is independent of an incoming {@link BeginGetCredentialOption}
+ * request. Providers must use this constructor for constructing entries to be registered
+ * with the framework outside of the span of an API call.
+ *
+ * @param type the type of the credential
+ * @param slice the slice containing the metadata to be shown on the UI. Must be
+ * constructed through the androidx.credentials jetpack library.
+ *
+ * @hide
+ */
+ // TODO: Unhide this constructor when the registry APIs are stable
public CredentialEntry(@NonNull String type, @NonNull Slice slice) {
- mType = type;
- mSlice = slice;
+ mBeginGetCredentialOption = null;
+ mType = requireNonNull(type, "type must not be null");
+ mSlice = requireNonNull(slice, "slice must not be null");
}
private CredentialEntry(@NonNull Parcel in) {
mType = in.readString8();
mSlice = in.readTypedObject(Slice.CREATOR);
+ mBeginGetCredentialOption = in.readTypedObject(BeginGetCredentialOption.CREATOR);
}
@NonNull
@@ -85,6 +135,15 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mType);
dest.writeTypedObject(mSlice, flags);
+ dest.writeTypedObject(mBeginGetCredentialOption, flags);
+ }
+
+ /**
+ * Returns the request option for which this credential entry has been constructed.
+ */
+ @NonNull
+ public BeginGetCredentialOption getBeginGetCredentialOption() {
+ return mBeginGetCredentialOption;
}
/**
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index b98deba..f0f4ad2 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -213,7 +213,7 @@
DEFAULT_FLAGS.put(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM, "false");
DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false");
DEFAULT_FLAGS.put(SETTINGS_FLASH_ALERTS, "false");
- DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "false");
+ DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "false");
}
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 3619c7b..685bd9a 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -277,8 +277,7 @@
inflater.inflate(R.menu.language_selection_list, menu);
final MenuItem searchMenuItem = menu.findItem(R.id.locale_search_menu);
- if (mLocalePickerCollector.hasSpecificPackageName()
- && mOnActionExpandListener != null) {
+ if (mOnActionExpandListener != null) {
searchMenuItem.setOnActionExpandListener(mOnActionExpandListener);
}
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 0704da4f..5bc8c04 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -944,10 +944,11 @@
}
/**
- * Check if the HAL is an AIDL implementation
+ * Check if the HAL is an AIDL implementation. For CTS testing purpose.
*
* @hide
*/
+ @TestApi
public boolean isAidlHal() {
return mICas != null;
}
diff --git a/media/java/android/media/MediaDescrambler.java b/media/java/android/media/MediaDescrambler.java
index 15dee85..3c276d6 100644
--- a/media/java/android/media/MediaDescrambler.java
+++ b/media/java/android/media/MediaDescrambler.java
@@ -17,6 +17,7 @@
package android.media;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.hardware.cas.IDescrambler;
import android.hardware.cas.ScramblingControl;
import android.hardware.cas.V1_0.IDescramblerBase;
@@ -221,10 +222,11 @@
}
/**
- * Check if the underlying HAL is AIDL. Used only for CTS.
+ * Check if the underlying HAL is AIDL. For CTS testing purpose.
*
* @hide
*/
+ @TestApi
public boolean isAidlHal() {
return mIsAidlHal;
}
diff --git a/media/java/android/media/tv/tuner/frontend/IptvFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/IptvFrontendCapabilities.java
new file mode 100644
index 0000000..b04fe17
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IptvFrontendCapabilities.java
@@ -0,0 +1,42 @@
+/*
+ * 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.media.tv.tuner.frontend;
+
+import android.annotation.SystemApi;
+
+/**
+ * IPTV Capabilities.
+ *
+ * @hide
+ */
+@SystemApi
+public class IptvFrontendCapabilities extends FrontendCapabilities {
+ private final int mProtocolCap;
+
+ // Used by native code
+ private IptvFrontendCapabilities(int protocolCap) {
+ mProtocolCap = protocolCap;
+ }
+
+ /**
+ * Gets the protocols of IPTV transmission (UDP/RTP) defined in
+ * {@link IptvFrontendSettings}.
+ */
+ public int getProtocolCapability() {
+ return mProtocolCap;
+ }
+}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index f56e236..7f4c03b 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1617,6 +1617,15 @@
interleaveModeCap, codeRateCap, bandwidthCap);
}
+jobject JTuner::getIptvFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IptvFrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(I)V");
+
+ jint protocolCap = caps.get<FrontendCapabilities::Tag::iptvCaps>()->protocolCap;
+
+ return env->NewObject(clazz, capsInit, protocolCap);
+}
+
jobject JTuner::getFrontendInfo(int id) {
shared_ptr<FrontendInfo> feInfo;
feInfo = sTunerClient->getFrontendInfo(id);
@@ -1695,6 +1704,11 @@
jcaps = getDtmbFrontendCaps(env, caps);
}
break;
+ case FrontendType::IPTV:
+ if (FrontendCapabilities::Tag::iptvCaps == caps.getTag()) {
+ jcaps = getIptvFrontendCaps(env, caps);
+ }
+ break;
default:
break;
}
@@ -3518,17 +3532,18 @@
static FrontendIptvSettingsFec getIptvFrontendSettingsFec(JNIEnv *env, const jobject &settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IptvFrontendSettings");
- jobject fec = env->GetObjectField(settings, env->GetFieldID(clazz, "mFec",
- "[Landroid/media/tv/tuner/frontend/IptvFrontendSettingsFec;"));
jclass fecClazz = env->FindClass("android/media/tv/tuner/frontend/IptvFrontendSettingsFec");
- FrontendIptvSettingsFecType fecType =
+ jobject fec = env->GetObjectField(settings, env->GetFieldID(clazz, "mFec",
+ "Landroid/media/tv/tuner/frontend/IptvFrontendSettingsFec;"));
+
+ FrontendIptvSettingsFecType type =
static_cast<FrontendIptvSettingsFecType>(
- env->GetIntField(fec, env->GetFieldID(fecClazz, "mFec", "I")));
+ env->GetIntField(fec, env->GetFieldID(fecClazz, "mFecType", "I")));
int32_t fecColNum = env->GetIntField(fec, env->GetFieldID(fecClazz, "mFecColNum", "I"));
int32_t fecRowNum = env->GetIntField(fec, env->GetFieldID(fecClazz, "mFecRowNum", "I"));
- FrontendIptvSettingsFec frontendIptvSettingsFec {
- .type = fecType,
+ FrontendIptvSettingsFec frontendIptvSettingsFec = {
+ .type = type,
.fecColNum = fecColNum,
.fecRowNum = fecRowNum,
};
@@ -3538,31 +3553,36 @@
static FrontendSettings getIptvFrontendSettings(JNIEnv *env, const jobject &settings) {
FrontendSettings frontendSettings;
- const char *clazzName = "android/media/tv/tuner/frontend/IptvFrontendSettings";
- jclass clazz = env->FindClass(clazzName);
+ const char *className = "android/media/tv/tuner/frontend/IptvFrontendSettings";
+ jclass clazz = env->FindClass(className);
FrontendIptvSettingsProtocol protocol =
static_cast<FrontendIptvSettingsProtocol>(
env->GetIntField(settings, env->GetFieldID(clazz, "mProtocol", "I")));
FrontendIptvSettingsIgmp igmp =
static_cast<FrontendIptvSettingsIgmp>(
env->GetIntField(settings, env->GetFieldID(clazz, "mIgmp", "I")));
- FrontendIptvSettingsFec fec = getIptvFrontendSettingsFec(env, settings);
int64_t bitrate = env->GetIntField(settings, env->GetFieldID(clazz, "mBitrate", "J"));
- jstring contentUrlJString = (jstring) env->GetObjectField(settings, env->GetFieldID(
- clazz, "mContentUrl",
- "[Landroid/media/tv/tuner/frontend/IptvFrontendSettings;"));
- const char *contentUrl = env->GetStringUTFChars(contentUrlJString, 0);
- DemuxIpAddress ipAddr = getDemuxIpAddress(env, settings, clazzName);
+ jstring jContentUrl = (jstring) env->GetObjectField(settings, env->GetFieldID(
+ clazz, "mContentUrl", "Ljava/lang/String;"));
+ const char *contentUrl = env->GetStringUTFChars(jContentUrl, 0);
+ DemuxIpAddress ipAddr = getDemuxIpAddress(env, settings, className);
FrontendIptvSettings frontendIptvSettings{
.protocol = protocol,
- .fec = fec,
.igmp = igmp,
.bitrate = bitrate,
.ipAddr = ipAddr,
.contentUrl = contentUrl,
};
+
+ jobject jFec = env->GetObjectField(settings, env->GetFieldID(clazz, "mFec",
+ "Landroid/media/tv/tuner/frontend/IptvFrontendSettingsFec;"));
+ if (jFec != nullptr) {
+ frontendIptvSettings.fec = getIptvFrontendSettingsFec(env, settings);
+ }
+
frontendSettings.set<FrontendSettings::Tag::iptv>(frontendIptvSettings);
+ env->ReleaseStringUTFChars(jContentUrl, contentUrl);
return frontendSettings;
}
@@ -3879,7 +3899,6 @@
return tuner->openLnbByName(name);
}
-
static jobject android_media_tv_Tuner_open_filter(
JNIEnv *env, jobject thiz, jint type, jint subType, jlong bufferSize) {
sp<JTuner> tuner = getTuner(env, thiz);
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 4069aaf..2bb14f6 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -236,6 +236,7 @@
static jobject getIsdbsFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
static jobject getIsdbtFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
static jobject getDtmbFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
+ static jobject getIptvFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
};
class C2DataIdInfo : public C2Param {
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Cat.java b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
index cd59a73..bf09bc7 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/Cat.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
@@ -258,7 +258,7 @@
Notification.BubbleMetadata bubbs = new Notification.BubbleMetadata.Builder()
.setIntent(
- PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE))
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_MUTABLE))
.setIcon(notificationIcon)
.setSuppressNotification(false)
.setDesiredHeight(context.getResources().getDisplayMetrics().heightPixels)
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 1283f81..fd41f5f 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -246,26 +246,28 @@
@Override
public void onActivityResult(int request, int result, Intent data) {
- if (request == REQUEST_TRUST_EXTERNAL_SOURCE && result == RESULT_OK) {
- // The user has just allowed this package to install other packages (via Settings).
- mAllowUnknownSources = true;
-
+ if (request == REQUEST_TRUST_EXTERNAL_SOURCE) {
// Log the fact that the app is requesting an install, and is now allowed to do it
// (before this point we could only log that it's requesting an install, but isn't
// allowed to do it yet).
String appOpStr =
AppOpsManager.permissionToOp(Manifest.permission.REQUEST_INSTALL_PACKAGES);
- mAppOpsManager.noteOpNoThrow(appOpStr, mOriginatingUid, mOriginatingPackage,
- mCallingAttributionTag,
+ int appOpMode = mAppOpsManager.noteOpNoThrow(appOpStr, mOriginatingUid,
+ mOriginatingPackage, mCallingAttributionTag,
"Successfully started package installation activity");
-
- DialogFragment currentDialog =
- (DialogFragment) getFragmentManager().findFragmentByTag("dialog");
- if (currentDialog != null) {
- currentDialog.dismissAllowingStateLoss();
+ if (appOpMode == AppOpsManager.MODE_ALLOWED) {
+ // The user has just allowed this package to install other packages
+ // (via Settings).
+ mAllowUnknownSources = true;
+ DialogFragment currentDialog =
+ (DialogFragment) getFragmentManager().findFragmentByTag("dialog");
+ if (currentDialog != null) {
+ currentDialog.dismissAllowingStateLoss();
+ }
+ initiateInstall();
+ } else {
+ finish();
}
-
- initiateInstall();
} else {
finish();
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 0228142..b92b3d6 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1316,7 +1316,7 @@
<!-- Name of the phone device with an active remote session. [CHAR LIMIT=30] -->
<string name="media_transfer_this_phone">This phone</string>
<!-- Sub status indicates device is not available due to an unknown error. [CHAR LIMIT=NONE] -->
- <string name="media_output_status_unknown_error">Unavailable due to unknown error</string>
+ <string name="media_output_status_unknown_error">Can\’t play on this device</string>
<!-- Sub status indicates device need premium account. [CHAR LIMIT=NONE] -->
<string name="media_output_status_require_premium">Upgrade account to switch</string>
<!-- Sub status indicates device not support download content. [CHAR LIMIT=NONE] -->
@@ -1324,11 +1324,11 @@
<!-- Sub status indicates device need to wait after ad. [CHAR LIMIT=NONE] -->
<string name="media_output_status_try_after_ad">Try again after the ad</string>
<!-- Sub status indicates device is in low-power mode. [CHAR LIMIT=NONE] -->
- <string name="media_output_status_device_in_low_power_mode">Device in low power mode</string>
+ <string name="media_output_status_device_in_low_power_mode">Wake up device to play here</string>
<!-- Sub status indicates the device does not authorize the user. [CHAR LIMIT=NONE] -->
- <string name="media_output_status_unauthorized">Requires authorization</string>
+ <string name="media_output_status_unauthorized">Device not approved to play</string>
<!-- Sub status indicates the device does not support the current media track. [CHAR LIMIT=NONE] -->
- <string name="media_output_status_track_unsupported">Current media track not supported</string>
+ <string name="media_output_status_track_unsupported">Can\’t play this media here</string>
<!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
<string name="profile_connect_timeout_subtext">Problem connecting. Turn device off & back on</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 85d4fab..07bd9ec 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -646,7 +646,7 @@
List<RouteListingPreference.Item> itemList = routeListingPreference.getItems();
for (RouteListingPreference.Item item : itemList) {
// Put suggested devices on the top first before further organization
- if (item.getFlags() == RouteListingPreference.Item.FLAG_SUGGESTED) {
+ if ((item.getFlags() & RouteListingPreference.Item.FLAG_SUGGESTED) != 0) {
finalizedItemList.add(0, item);
} else {
finalizedItemList.add(item);
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
index 7897934..442c6fa 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
@@ -66,11 +66,28 @@
fun isPlaying(): Boolean = animator.isRunning
private fun applyConfigToShader() {
- rippleShader.setCenter(config.centerX, config.centerY)
- rippleShader.setMaxSize(config.maxWidth, config.maxHeight)
- rippleShader.rippleFill = config.shouldFillRipple
- rippleShader.pixelDensity = config.pixelDensity
- rippleShader.color = ColorUtils.setAlphaComponent(config.color, config.opacity)
- rippleShader.sparkleStrength = config.sparkleStrength
+ with(rippleShader) {
+ setCenter(config.centerX, config.centerY)
+ setMaxSize(config.maxWidth, config.maxHeight)
+ pixelDensity = config.pixelDensity
+ color = ColorUtils.setAlphaComponent(config.color, config.opacity)
+ sparkleStrength = config.sparkleStrength
+
+ assignFadeParams(baseRingFadeParams, config.baseRingFadeParams)
+ assignFadeParams(sparkleRingFadeParams, config.sparkleRingFadeParams)
+ assignFadeParams(centerFillFadeParams, config.centerFillFadeParams)
+ }
+ }
+
+ private fun assignFadeParams(
+ destFadeParams: RippleShader.FadeParams,
+ srcFadeParams: RippleShader.FadeParams?
+ ) {
+ srcFadeParams?.let {
+ destFadeParams.fadeInStart = it.fadeInStart
+ destFadeParams.fadeInEnd = it.fadeInEnd
+ destFadeParams.fadeOutStart = it.fadeOutStart
+ destFadeParams.fadeOutEnd = it.fadeOutEnd
+ }
}
}
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 773ac55..1786d13 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
@@ -20,8 +20,11 @@
val pixelDensity: Float = 1f,
var color: Int = Color.WHITE,
val opacity: Int = RIPPLE_DEFAULT_ALPHA,
- val shouldFillRipple: Boolean = false,
val sparkleStrength: Float = 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 {
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 74bc910..61ca90a 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
@@ -82,7 +82,7 @@
vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
float radius = in_size.x * 0.5;
float sparkleRing = soften(circleRing(p_distorted-in_center, radius), in_blur);
- float inside = soften(sdCircle(p_distorted-in_center, radius * 1.2), in_blur);
+ float inside = soften(sdCircle(p_distorted-in_center, radius * 1.25), in_blur);
float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
* (1.-sparkleRing) * in_fadeSparkle;
@@ -270,38 +270,6 @@
var currentHeight: Float = 0f
private set
- /**
- * True if the ripple should stayed filled in as it expands to give a filled-in circle effect.
- * False for a ring effect.
- *
- * <p>You must reset fade params after changing this.
- *
- * TODO(b/265326983): Remove this and only expose fade params.
- */
- var rippleFill: Boolean = false
- set(value) {
- if (value) {
- baseRingFadeParams.fadeOutStart = 1f
- baseRingFadeParams.fadeOutEnd = 1f
-
- centerFillFadeParams.fadeInStart = 0f
- centerFillFadeParams.fadeInEnd = 0f
- centerFillFadeParams.fadeOutStart = 1f
- centerFillFadeParams.fadeOutEnd = 1f
- } else {
- // Set back to the original fade parameters.
- // Ideally this should be set by the client as they know the initial value.
- baseRingFadeParams.fadeOutStart = DEFAULT_BASE_RING_FADE_OUT_START
- baseRingFadeParams.fadeOutEnd = DEFAULT_FADE_OUT_END
-
- centerFillFadeParams.fadeInStart = DEFAULT_FADE_IN_START
- centerFillFadeParams.fadeInEnd = DEFAULT_CENTER_FILL_FADE_IN_END
- centerFillFadeParams.fadeOutStart = DEFAULT_CENTER_FILL_FADE_OUT_START
- centerFillFadeParams.fadeOutEnd = DEFAULT_CENTER_FILL_FADE_OUT_END
- }
- field = value
- }
-
/** Parameters that are used to fade in/ out of the sparkle ring. */
val sparkleRingFadeParams =
FadeParams(
@@ -324,12 +292,7 @@
DEFAULT_FADE_OUT_END
)
- /**
- * Parameters that are used to fade in/ out of the center fill.
- *
- * <p>Note that if [rippleFill] is set to true, those will be ignored and the center fill will
- * be always full alpha.
- */
+ /** Parameters that are used to fade in/ out of the center fill. */
val centerFillFadeParams =
FadeParams(
DEFAULT_FADE_IN_START,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java
index 95675ce..209d5e8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java
@@ -38,13 +38,19 @@
public class Monitor {
private final String mTag = getClass().getSimpleName();
private final Executor mExecutor;
+ private final Set<Condition> mPreconditions;
private final HashMap<Condition, ArraySet<Subscription.Token>> mConditions = new HashMap<>();
private final HashMap<Subscription.Token, SubscriptionState> mSubscriptions = new HashMap<>();
private static class SubscriptionState {
private final Subscription mSubscription;
+
+ // A subscription must maintain a reference to any active nested subscription so that it may
+ // be later removed when the current subscription becomes invalid.
+ private Subscription.Token mNestedSubscriptionToken;
private Boolean mAllConditionsMet;
+ private boolean mActive;
SubscriptionState(Subscription subscription) {
mSubscription = subscription;
@@ -54,7 +60,27 @@
return mSubscription.mConditions;
}
- public void update() {
+ /**
+ * Signals that the {@link Subscription} is now being monitored and will receive updates
+ * based on its conditions.
+ */
+ private void setActive(boolean active) {
+ if (mActive == active) {
+ return;
+ }
+
+ mActive = active;
+
+ final Callback callback = mSubscription.getCallback();
+
+ if (callback == null) {
+ return;
+ }
+
+ callback.onActiveChanged(active);
+ }
+
+ public void update(Monitor monitor) {
final Boolean result = Evaluator.INSTANCE.evaluate(mSubscription.mConditions,
Evaluator.OP_AND);
// Consider unknown (null) as true
@@ -65,7 +91,50 @@
}
mAllConditionsMet = newAllConditionsMet;
- mSubscription.mCallback.onConditionsChanged(mAllConditionsMet);
+
+ final Subscription nestedSubscription = mSubscription.getNestedSubscription();
+
+ if (nestedSubscription != null) {
+ if (mAllConditionsMet && mNestedSubscriptionToken == null) {
+ // When all conditions are met for a subscription with a nested subscription
+ // that is not currently being monitored, add the nested subscription for
+ // monitor.
+ mNestedSubscriptionToken =
+ monitor.addSubscription(nestedSubscription, null);
+ } else if (!mAllConditionsMet && mNestedSubscriptionToken != null) {
+ // When conditions are not met and there is an active nested condition, remove
+ // the nested condition from monitoring.
+ removeNestedSubscription(monitor);
+ }
+ return;
+ }
+
+ mSubscription.getCallback().onConditionsChanged(mAllConditionsMet);
+ }
+
+ /**
+ * Invoked when the {@link Subscription} has been added to the {@link Monitor}.
+ */
+ public void onAdded() {
+ setActive(true);
+ }
+
+ /**
+ * Invoked when the {@link Subscription} has been removed from the {@link Monitor},
+ * allowing cleanup code to run.
+ */
+ public void onRemoved(Monitor monitor) {
+ setActive(false);
+ removeNestedSubscription(monitor);
+ }
+
+ private void removeNestedSubscription(Monitor monitor) {
+ if (mNestedSubscriptionToken == null) {
+ return;
+ }
+
+ monitor.removeSubscription(mNestedSubscriptionToken);
+ mNestedSubscriptionToken = null;
}
}
@@ -77,9 +146,20 @@
}
};
+ /**
+ * Constructor for injected use-cases. By default, no preconditions are present.
+ */
@Inject
public Monitor(@Main Executor executor) {
+ this(executor, Collections.emptySet());
+ }
+
+ /**
+ * Main constructor, allowing specifying preconditions.
+ */
+ public Monitor(Executor executor, Set<Condition> preconditions) {
mExecutor = executor;
+ mPreconditions = preconditions;
}
private void updateConditionMetState(Condition condition) {
@@ -91,7 +171,7 @@
return;
}
- subscriptions.stream().forEach(token -> mSubscriptions.get(token).update());
+ subscriptions.stream().forEach(token -> mSubscriptions.get(token).update(this));
}
/**
@@ -101,15 +181,25 @@
* @return A {@link Subscription.Token} that can be used to remove the subscription.
*/
public Subscription.Token addSubscription(@NonNull Subscription subscription) {
+ return addSubscription(subscription, mPreconditions);
+ }
+
+ private Subscription.Token addSubscription(@NonNull Subscription subscription,
+ Set<Condition> preconditions) {
+ // If preconditions are set on the monitor, set up as a nested condition.
+ final Subscription normalizedCondition = preconditions != null
+ ? new Subscription.Builder(subscription).addConditions(preconditions).build()
+ : subscription;
+
final Subscription.Token token = new Subscription.Token();
- final SubscriptionState state = new SubscriptionState(subscription);
+ final SubscriptionState state = new SubscriptionState(normalizedCondition);
mExecutor.execute(() -> {
if (shouldLog()) Log.d(mTag, "adding subscription");
mSubscriptions.put(token, state);
// Add and associate conditions.
- subscription.getConditions().stream().forEach(condition -> {
+ normalizedCondition.getConditions().stream().forEach(condition -> {
if (!mConditions.containsKey(condition)) {
mConditions.put(condition, new ArraySet<>());
condition.addCallback(mConditionCallback);
@@ -118,8 +208,10 @@
mConditions.get(condition).add(token);
});
+ state.onAdded();
+
// Update subscription state.
- state.update();
+ state.update(this);
});
return token;
@@ -139,7 +231,9 @@
return;
}
- mSubscriptions.remove(token).getConditions().forEach(condition -> {
+ final SubscriptionState removedSubscription = mSubscriptions.remove(token);
+
+ removedSubscription.getConditions().forEach(condition -> {
if (!mConditions.containsKey(condition)) {
Log.e(mTag, "condition not present:" + condition);
return;
@@ -153,6 +247,8 @@
mConditions.remove(condition);
}
});
+
+ removedSubscription.onRemoved(this);
});
}
@@ -168,12 +264,19 @@
private final Set<Condition> mConditions;
private final Callback mCallback;
- /**
- *
- */
- public Subscription(Set<Condition> conditions, Callback callback) {
+ // A nested {@link Subscription} is a special callback where the specified condition's
+ // active state is dependent on the conditions of the parent {@link Subscription} being met.
+ // Once active, the nested subscription's conditions are registered as normal with the
+ // monitor and its callback (which could also be a nested condition) is triggered based on
+ // those conditions. The nested condition will be removed from monitor if the outer
+ // subscription's conditions ever become invalid.
+ private final Subscription mNestedSubscription;
+
+ private Subscription(Set<Condition> conditions, Callback callback,
+ Subscription nestedSubscription) {
this.mConditions = Collections.unmodifiableSet(conditions);
this.mCallback = callback;
+ this.mNestedSubscription = nestedSubscription;
}
public Set<Condition> getConditions() {
@@ -184,6 +287,10 @@
return mCallback;
}
+ public Subscription getNestedSubscription() {
+ return mNestedSubscription;
+ }
+
/**
* A {@link Token} is an identifier that is associated with a {@link Subscription} which is
* registered with a {@link Monitor}.
@@ -196,14 +303,26 @@
*/
public static class Builder {
private final Callback mCallback;
+ private final Subscription mNestedSubscription;
private final ArraySet<Condition> mConditions;
+ private final ArraySet<Condition> mPreconditions;
/**
* Default constructor specifying the {@link Callback} for the {@link Subscription}.
*/
public Builder(Callback callback) {
+ this(null, callback);
+ }
+
+ public Builder(Subscription nestedSubscription) {
+ this(nestedSubscription, null);
+ }
+
+ private Builder(Subscription nestedSubscription, Callback callback) {
+ mNestedSubscription = nestedSubscription;
mCallback = callback;
- mConditions = new ArraySet<>();
+ mConditions = new ArraySet();
+ mPreconditions = new ArraySet();
}
/**
@@ -217,11 +336,38 @@
}
/**
+ * Adds a set of {@link Condition} to be a precondition for {@link Subscription}.
+ *
+ * @return The updated {@link Builder}.
+ */
+ public Builder addPreconditions(Set<Condition> condition) {
+ if (condition == null) {
+ return this;
+ }
+ mPreconditions.addAll(condition);
+ return this;
+ }
+
+ /**
+ * Adds a {@link Condition} to be a precondition for {@link Subscription}.
+ *
+ * @return The updated {@link Builder}.
+ */
+ public Builder addPrecondition(Condition condition) {
+ mPreconditions.add(condition);
+ return this;
+ }
+
+ /**
* Adds a set of {@link Condition} to be associated with the {@link Subscription}.
*
* @return The updated {@link Builder}.
*/
public Builder addConditions(Set<Condition> condition) {
+ if (condition == null) {
+ return this;
+ }
+
mConditions.addAll(condition);
return this;
}
@@ -232,7 +378,11 @@
* @return The resulting {@link Subscription}.
*/
public Subscription build() {
- return new Subscription(mConditions, mCallback);
+ final Subscription subscription =
+ new Subscription(mConditions, mCallback, mNestedSubscription);
+ return !mPreconditions.isEmpty()
+ ? new Subscription(mPreconditions, null, subscription)
+ : subscription;
}
}
}
@@ -255,5 +405,13 @@
* only partial conditions have been fulfilled.
*/
void onConditionsChanged(boolean allConditionsMet);
+
+ /**
+ * Called when the active state of the {@link Subscription} changes.
+ * @param active {@code true} when changes to the conditions will affect the
+ * {@link Subscription}, {@code false} otherwise.
+ */
+ default void onActiveChanged(boolean active) {
+ }
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
index 9b73cc3..bd20777 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
@@ -25,7 +25,7 @@
import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows
/** Extension of [TextView] which draws two shadows on the text (ambient and key shadows} */
-class DoubleShadowTextView
+open class DoubleShadowTextView
@JvmOverloads
constructor(
context: Context,
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index a70b4cd..1254e1e 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -24,8 +24,8 @@
import android.text.format.DateFormat
import android.util.TypedValue
import android.view.View
-import android.widget.FrameLayout
import android.view.ViewTreeObserver
+import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -40,8 +40,8 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.log.dagger.KeyguardLargeClockLog
+import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockTickRate
@@ -53,22 +53,24 @@
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
+import java.util.Locale
+import java.util.TimeZone
+import java.util.concurrent.Executor
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
-import java.util.Locale
-import java.util.TimeZone
-import java.util.concurrent.Executor
-import javax.inject.Inject
/**
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
* [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
*/
-open class ClockEventController @Inject constructor(
+open class ClockEventController
+@Inject
+constructor(
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val broadcastDispatcher: BroadcastDispatcher,
@@ -115,52 +117,59 @@
private var disposableHandle: DisposableHandle? = null
private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)
- private val mLayoutChangedListener = object : View.OnLayoutChangeListener {
- private var currentSmallClockView: View? = null
- private var currentLargeClockView: View? = null
- private var currentSmallClockLocation = IntArray(2)
- private var currentLargeClockLocation = IntArray(2)
+ private val mLayoutChangedListener =
+ object : View.OnLayoutChangeListener {
+ private var currentSmallClockView: View? = null
+ private var currentLargeClockView: View? = null
+ private var currentSmallClockLocation = IntArray(2)
+ private var currentLargeClockLocation = IntArray(2)
- override fun onLayoutChange(
- view: View?,
- left: Int,
- top: Int,
- right: Int,
- bottom: Int,
- oldLeft: Int,
- oldTop: Int,
- oldRight: Int,
- oldBottom: Int
- ) {
- val parent = (view?.parent) as FrameLayout
+ override fun onLayoutChange(
+ view: View?,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ val parent = (view?.parent) as FrameLayout
- // don't pass in negative bounds when clocks are in transition state
- if (view.locationOnScreen[0] < 0 || view.locationOnScreen[1] < 0) {
- return
- }
+ // don't pass in negative bounds when clocks are in transition state
+ if (view.locationOnScreen[0] < 0 || view.locationOnScreen[1] < 0) {
+ return
+ }
- // SMALL CLOCK
- if (parent.id == R.id.lockscreen_clock_view) {
- // view bounds have changed due to clock size changing (i.e. different character widths)
- // AND/OR the view has been translated when transitioning between small and large clock
- if (view != currentSmallClockView ||
- !view.locationOnScreen.contentEquals(currentSmallClockLocation)) {
- currentSmallClockView = view
- currentSmallClockLocation = view.locationOnScreen
- updateRegionSampler(view)
+ // SMALL CLOCK
+ if (parent.id == R.id.lockscreen_clock_view) {
+ // view bounds have changed due to clock size changing (i.e. different character
+ // widths)
+ // AND/OR the view has been translated when transitioning between small and
+ // large clock
+ if (
+ view != currentSmallClockView ||
+ !view.locationOnScreen.contentEquals(currentSmallClockLocation)
+ ) {
+ currentSmallClockView = view
+ currentSmallClockLocation = view.locationOnScreen
+ updateRegionSampler(view)
+ }
+ }
+ // LARGE CLOCK
+ else if (parent.id == R.id.lockscreen_clock_view_large) {
+ if (
+ view != currentLargeClockView ||
+ !view.locationOnScreen.contentEquals(currentLargeClockLocation)
+ ) {
+ currentLargeClockView = view
+ currentLargeClockLocation = view.locationOnScreen
+ updateRegionSampler(view)
+ }
+ }
}
}
- // LARGE CLOCK
- else if (parent.id == R.id.lockscreen_clock_view_large) {
- if (view != currentLargeClockView ||
- !view.locationOnScreen.contentEquals(currentLargeClockLocation)) {
- currentLargeClockView = view
- currentLargeClockLocation = view.locationOnScreen
- updateRegionSampler(view)
- }
- }
- }
- }
private fun updateColors() {
val wallpaperManager = WallpaperManager.getInstance(context)
@@ -189,30 +198,33 @@
private fun updateRegionSampler(sampledRegion: View) {
regionSampler?.stopRegionSampler()
- regionSampler = createRegionSampler(
- sampledRegion,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- ::updateColors
- )?.apply { startRegionSampler() }
+ regionSampler =
+ createRegionSampler(
+ sampledRegion,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ ::updateColors
+ )
+ ?.apply { startRegionSampler() }
updateColors()
}
protected open fun createRegionSampler(
- sampledView: View?,
- mainExecutor: Executor?,
- bgExecutor: Executor?,
- regionSamplingEnabled: Boolean,
- updateColors: () -> Unit
+ sampledView: View?,
+ mainExecutor: Executor?,
+ bgExecutor: Executor?,
+ regionSamplingEnabled: Boolean,
+ updateColors: () -> Unit
): RegionSampler? {
return RegionSampler(
sampledView,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
- updateColors)
+ updateColors
+ )
}
var regionSampler: RegionSampler? = null
@@ -224,64 +236,68 @@
private var smallClockIsDark = true
private var largeClockIsDark = true
- private val configListener = object : ConfigurationController.ConfigurationListener {
- override fun onThemeChanged() {
- clock?.events?.onColorPaletteChanged(resources)
- updateColors()
- }
-
- override fun onDensityOrFontScaleChanged() {
- updateFontSizes()
- }
- }
-
- private val batteryCallback = object : BatteryStateChangeCallback {
- override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
- if (isKeyguardVisible && !isCharging && charging) {
- clock?.animations?.charge()
+ private val configListener =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onThemeChanged() {
+ clock?.events?.onColorPaletteChanged(resources)
+ updateColors()
}
- isCharging = charging
- }
- }
- private val localeBroadcastReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- clock?.events?.onLocaleChanged(Locale.getDefault())
+ override fun onDensityOrFontScaleChanged() {
+ updateFontSizes()
+ }
}
- }
- private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
- override fun onKeyguardVisibilityChanged(visible: Boolean) {
- isKeyguardVisible = visible
- if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
- if (!isKeyguardVisible) {
- clock?.animations?.doze(if (isDozing) 1f else 0f)
+ private val batteryCallback =
+ object : BatteryStateChangeCallback {
+ override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
+ if (isKeyguardVisible && !isCharging && charging) {
+ clock?.animations?.charge()
+ }
+ isCharging = charging
+ }
+ }
+
+ private val localeBroadcastReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ clock?.events?.onLocaleChanged(Locale.getDefault())
+ }
+ }
+
+ private val keyguardUpdateMonitorCallback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onKeyguardVisibilityChanged(visible: Boolean) {
+ isKeyguardVisible = visible
+ if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
+ if (!isKeyguardVisible) {
+ clock?.animations?.doze(if (isDozing) 1f else 0f)
+ }
+ }
+
+ smallTimeListener?.update(shouldTimeListenerRun)
+ largeTimeListener?.update(shouldTimeListenerRun)
+ }
+
+ override fun onTimeFormatChanged(timeFormat: String) {
+ clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ }
+
+ override fun onTimeZoneChanged(timeZone: TimeZone) {
+ clock?.events?.onTimeZoneChanged(timeZone)
+ }
+
+ override fun onUserSwitchComplete(userId: Int) {
+ clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ }
+
+ override fun onWeatherDataChanged(data: Weather?) {
+ if (data != null) {
+ clock?.events?.onWeatherDataChanged(data)
}
}
-
- smallTimeListener?.update(shouldTimeListenerRun)
- largeTimeListener?.update(shouldTimeListenerRun)
}
- override fun onTimeFormatChanged(timeFormat: String) {
- clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
- }
-
- override fun onTimeZoneChanged(timeZone: TimeZone) {
- clock?.events?.onTimeZoneChanged(timeZone)
- }
-
- override fun onUserSwitchComplete(userId: Int) {
- clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
- }
-
- override fun onWeatherDataChanged(data: Weather?) {
- if (data != null) {
- clock?.events?.onWeatherDataChanged(data)
- }
- }
- }
-
fun registerListeners(parent: View) {
if (isRegistered) {
return
@@ -295,17 +311,18 @@
configurationController.addCallback(configListener)
batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
- disposableHandle = parent.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- listenForDozing(this)
- if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
- listenForDozeAmountTransition(this)
- listenForAnyStateToAodTransition(this)
- } else {
- listenForDozeAmount(this)
+ disposableHandle =
+ parent.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ listenForDozing(this)
+ if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
+ listenForDozeAmountTransition(this)
+ listenForAnyStateToAodTransition(this)
+ } else {
+ listenForDozeAmount(this)
+ }
}
}
- }
smallTimeListener?.update(shouldTimeListenerRun)
largeTimeListener?.update(shouldTimeListenerRun)
}
@@ -344,10 +361,18 @@
}
private fun updateFontSizes() {
- clock?.smallClock?.events?.onFontSettingChanged(
- resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat())
- clock?.largeClock?.events?.onFontSettingChanged(
- resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat())
+ clock
+ ?.smallClock
+ ?.events
+ ?.onFontSettingChanged(
+ resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
+ )
+ clock
+ ?.largeClock
+ ?.events
+ ?.onFontSettingChanged(
+ resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
+ )
}
private fun handleDoze(doze: Float) {
@@ -359,68 +384,59 @@
@VisibleForTesting
internal fun listenForDozeAmount(scope: CoroutineScope): Job {
- return scope.launch {
- keyguardInteractor.dozeAmount.collect {
- handleDoze(it)
- }
- }
+ return scope.launch { keyguardInteractor.dozeAmount.collect { handleDoze(it) } }
}
@VisibleForTesting
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
return scope.launch {
- keyguardTransitionInteractor.dozeAmountTransition.collect {
- handleDoze(it.value)
- }
+ keyguardTransitionInteractor.dozeAmountTransition.collect { handleDoze(it.value) }
}
}
/**
- * When keyguard is displayed again after being gone, the clock must be reset to full
- * dozing.
+ * When keyguard is displayed again after being gone, the clock must be reset to full dozing.
*/
@VisibleForTesting
internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
return scope.launch {
- keyguardTransitionInteractor.anyStateToAodTransition.filter {
- it.transitionState == TransitionState.FINISHED
- }.collect {
- handleDoze(1f)
- }
+ keyguardTransitionInteractor.anyStateToAodTransition
+ .filter { it.transitionState == TransitionState.FINISHED }
+ .collect { handleDoze(1f) }
}
}
@VisibleForTesting
internal fun listenForDozing(scope: CoroutineScope): Job {
return scope.launch {
- combine (
- keyguardInteractor.dozeAmount,
- keyguardInteractor.isDozing,
- ) { localDozeAmount, localIsDozing ->
- localDozeAmount > dozeAmount || localIsDozing
- }
- .collect { localIsDozing ->
- isDozing = localIsDozing
- }
+ combine(
+ keyguardInteractor.dozeAmount,
+ keyguardInteractor.isDozing,
+ ) { localDozeAmount, localIsDozing ->
+ localDozeAmount > dozeAmount || localIsDozing
+ }
+ .collect { localIsDozing -> isDozing = localIsDozing }
}
}
class TimeListener(val clockFace: ClockFaceController, val executor: DelayableExecutor) {
- val predrawListener = ViewTreeObserver.OnPreDrawListener {
- clockFace.events.onTimeTick()
- true
- }
-
- val secondsRunnable = object : Runnable {
- override fun run() {
- if (!isRunning) {
- return
- }
-
- executor.executeDelayed(this, 990)
+ val predrawListener =
+ ViewTreeObserver.OnPreDrawListener {
clockFace.events.onTimeTick()
+ true
}
- }
+
+ val secondsRunnable =
+ object : Runnable {
+ override fun run() {
+ if (!isRunning) {
+ return
+ }
+
+ executor.executeDelayed(this, 990)
+ clockFace.events.onTimeTick()
+ }
+ }
var isRunning: Boolean = false
private set
@@ -432,7 +448,9 @@
isRunning = true
when (clockFace.events.tickRate) {
- ClockTickRate.PER_MINUTE -> {/* Handled by KeyguardClockSwitchController */}
+ ClockTickRate.PER_MINUTE -> {
+ /* Handled by KeyguardClockSwitchController */
+ }
ClockTickRate.PER_SECOND -> executor.execute(secondsRunnable)
ClockTickRate.PER_FRAME -> {
clockFace.view.viewTreeObserver.addOnPreDrawListener(predrawListener)
@@ -442,7 +460,9 @@
}
fun stop() {
- if (!isRunning) { return }
+ if (!isRunning) {
+ return
+ }
isRunning = false
clockFace.view.viewTreeObserver.removeOnPreDrawListener(predrawListener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 3d39da6..7e86a5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -22,6 +22,8 @@
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
@@ -31,7 +33,6 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.CommandQueue.Callbacks
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -41,7 +42,9 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
/**
* Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -52,6 +55,7 @@
constructor(
private val repository: KeyguardRepository,
private val commandQueue: CommandQueue,
+ featureFlags: FeatureFlags,
) {
/**
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
@@ -129,6 +133,29 @@
*/
val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
+ /** Keyguard is present and is not occluded. */
+ val isKeyguardVisible: Flow<Boolean> =
+ combine(isKeyguardShowing, isKeyguardOccluded) { showing, occluded -> showing && !occluded }
+
+ /** Whether camera is launched over keyguard. */
+ var isSecureCameraActive =
+ if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
+ combine(
+ isKeyguardVisible,
+ repository.isBouncerShowing,
+ onCameraLaunchDetected,
+ ) { isKeyguardVisible, isBouncerShowing, cameraLaunchEvent ->
+ when {
+ isKeyguardVisible -> false
+ isBouncerShowing -> false
+ else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP
+ }
+ }
+ .onStart { emit(false) }
+ } else {
+ flowOf(false)
+ }
+
/** The approximate location on the screen of the fingerprint sensor, if one is available. */
val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 1d000eb..6076e58 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -1139,8 +1139,10 @@
/* pixelDensity= */ getContext().getResources().getDisplayMetrics().density,
mColorSchemeTransition.getAccentPrimary().getCurrentColor(),
/* opacity= */ 100,
- /* shouldFillRipple= */ false,
/* sparkleStrength= */ 0f,
+ /* baseRingFadeParams= */ null,
+ /* sparkleRingFadeParams= */ null,
+ /* centerFillFadeParams= */ null,
/* shouldDistort= */ false
)
);
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index 997370b..4ff082a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -119,13 +119,19 @@
private fun removeRippleFill() {
with(rippleShader) {
+ // Set back to default because we modified them in [setupRippleFadeParams].
baseRingFadeParams.fadeOutStart = RippleShader.DEFAULT_BASE_RING_FADE_OUT_START
baseRingFadeParams.fadeOutEnd = RippleShader.DEFAULT_FADE_OUT_END
centerFillFadeParams.fadeInStart = RippleShader.DEFAULT_FADE_IN_START
centerFillFadeParams.fadeInEnd = RippleShader.DEFAULT_CENTER_FILL_FADE_IN_END
- centerFillFadeParams.fadeOutStart = RippleShader.DEFAULT_CENTER_FILL_FADE_OUT_START
- centerFillFadeParams.fadeOutEnd = RippleShader.DEFAULT_CENTER_FILL_FADE_OUT_END
+
+ // To avoid a seam showing up, we should match either:
+ // 1. baseRingFadeParams#fadeInEnd and centerFillFadeParams#fadeOutStart
+ // 2. baseRingFadeParams#fadeOutStart and centerFillFadeOutStart
+ // Here we go with 1 to fade in the centerFill faster.
+ centerFillFadeParams.fadeOutStart = baseRingFadeParams.fadeInEnd
+ centerFillFadeParams.fadeOutEnd = RippleShader.DEFAULT_FADE_OUT_END
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
new file mode 100644
index 0000000..7db293d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
@@ -0,0 +1,27 @@
+/*
+ * 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.process;
+
+/**
+ * A simple wrapper that provides access to process-related details. This facilitates testing by
+ * providing a mockable target around these details.
+ */
+public class ProcessWrapper {
+ public int getUserHandleIdentifier() {
+ return android.os.Process.myUserHandle().getIdentifier();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/process/condition/UserProcessCondition.java b/packages/SystemUI/src/com/android/systemui/process/condition/UserProcessCondition.java
new file mode 100644
index 0000000..5a21ea0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/process/condition/UserProcessCondition.java
@@ -0,0 +1,48 @@
+/*
+ * 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.process.condition;
+
+import com.android.systemui.process.ProcessWrapper;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.condition.Condition;
+
+import javax.inject.Inject;
+
+/**
+ * {@link UserProcessCondition} provides a signal when the process handle belongs to the current
+ * user.
+ */
+public class UserProcessCondition extends Condition {
+ private final ProcessWrapper mProcessWrapper;
+ private final UserTracker mUserTracker;
+
+ @Inject
+ public UserProcessCondition(ProcessWrapper processWrapper, UserTracker userTracker) {
+ mProcessWrapper = processWrapper;
+ mUserTracker = userTracker;
+ }
+
+ @Override
+ protected void start() {
+ updateCondition(mUserTracker.getUserId()
+ == mProcessWrapper.getUserHandleIdentifier());
+ }
+
+ @Override
+ protected void stop() {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java b/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
new file mode 100644
index 0000000..b41bca0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
@@ -0,0 +1,84 @@
+/*
+ * 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.util.condition;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
+
+import java.util.Set;
+
+/**
+ * {@link ConditionalCoreStartable} is a {@link com.android.systemui.CoreStartable} abstract
+ * implementation where conditions must be met before routines are executed.
+ */
+public abstract class ConditionalCoreStartable implements CoreStartable {
+ private final Monitor mMonitor;
+ private final Set<Condition> mConditionSet;
+ private Monitor.Subscription.Token mStartToken;
+ private Monitor.Subscription.Token mBootCompletedToken;
+
+ public ConditionalCoreStartable(Monitor monitor) {
+ this(monitor, null);
+ }
+
+ public ConditionalCoreStartable(Monitor monitor, Set<Condition> conditionSet) {
+ mMonitor = monitor;
+ mConditionSet = conditionSet;
+ }
+
+ @Override
+ public final void start() {
+ if (mConditionSet == null || mConditionSet.isEmpty()) {
+ onStart();
+ return;
+ }
+
+ mStartToken = mMonitor.addSubscription(
+ new Monitor.Subscription.Builder(allConditionsMet -> {
+ if (allConditionsMet) {
+ mMonitor.removeSubscription(mStartToken);
+ mStartToken = null;
+ onStart();
+ }
+ }).addConditions(mConditionSet)
+ .build());
+ }
+
+ protected abstract void onStart();
+
+ @Override
+ public final void onBootCompleted() {
+ if (mConditionSet == null || mConditionSet.isEmpty()) {
+ bootCompleted();
+ return;
+ }
+
+ mBootCompletedToken = mMonitor.addSubscription(
+ new Monitor.Subscription.Builder(allConditionsMet -> {
+ if (allConditionsMet) {
+ mMonitor.removeSubscription(mBootCompletedToken);
+ mBootCompletedToken = null;
+ bootCompleted();
+ }
+ }).addConditions(mConditionSet)
+ .build());
+ }
+
+ protected void bootCompleted() {
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 43a2017..f7fec80 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -58,7 +58,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
-import java.util.*
+import java.util.TimeZone
import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever
@@ -105,7 +105,9 @@
repository = FakeKeyguardRepository()
underTest = ClockEventController(
- KeyguardInteractor(repository = repository, commandQueue = commandQueue),
+ KeyguardInteractor(repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags),
KeyguardTransitionInteractor(repository = transitionRepository),
broadcastDispatcher,
batteryController,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 05bd1e4..3d0d036 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -159,7 +159,9 @@
mAuthRippleController,
mResources,
new KeyguardTransitionInteractor(mTransitionRepository),
- new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue),
+ new KeyguardInteractor(new FakeKeyguardRepository(),
+ mCommandQueue,
+ mFeatureFlags),
mFeatureFlags
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 9866163..bb037642 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -21,12 +21,9 @@
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
-import android.provider.Settings
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater
@@ -106,7 +103,6 @@
@Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var udfpsView: UdfpsView
- @Mock private lateinit var udfpsEnrollView: UdfpsEnrollView
@Mock private lateinit var udfpsKeyguardView: UdfpsKeyguardView
@Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
@Mock private lateinit var featureFlags: FeatureFlags
@@ -121,18 +117,14 @@
@Before
fun setup() {
- context.orCreateTestableResources.addOverride(R.integer.config_udfpsEnrollProgressBar, 20)
whenever(inflater.inflate(R.layout.udfps_view, null, false))
.thenReturn(udfpsView)
- whenever(inflater.inflate(R.layout.udfps_enroll_view, null))
- .thenReturn(udfpsEnrollView)
whenever(inflater.inflate(R.layout.udfps_bp_view, null))
.thenReturn(mock(UdfpsBpView::class.java))
whenever(inflater.inflate(R.layout.udfps_keyguard_view, null))
.thenReturn(udfpsKeyguardView)
whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null))
.thenReturn(mock(UdfpsFpmEmptyView::class.java))
- whenever(udfpsEnrollView.context).thenReturn(context)
}
private fun withReason(
@@ -162,37 +154,6 @@
}
@Test
- fun showUdfpsOverlay_settings() = withReason(REASON_AUTH_SETTINGS) { showUdfpsOverlay() }
-
- @Test
- fun showUdfpsOverlay_locate() = withReason(REASON_ENROLL_FIND_SENSOR) {
- showUdfpsOverlay(isEnrollUseCase = true)
- }
-
- @Test
- fun showUdfpsOverlay_locate_withEnrollmentUiRemoved() {
- Settings.Global.putInt(mContext.contentResolver, SETTING_REMOVE_ENROLLMENT_UI, 1)
- withReason(REASON_ENROLL_FIND_SENSOR, isDebuggable = true) {
- showUdfpsOverlay(isEnrollUseCase = false)
- }
- Settings.Global.putInt(mContext.contentResolver, SETTING_REMOVE_ENROLLMENT_UI, 0)
- }
-
- @Test
- fun showUdfpsOverlay_enroll() = withReason(REASON_ENROLL_ENROLLING) {
- showUdfpsOverlay(isEnrollUseCase = true)
- }
-
- @Test
- fun showUdfpsOverlay_enroll_withEnrollmentUiRemoved() {
- Settings.Global.putInt(mContext.contentResolver, SETTING_REMOVE_ENROLLMENT_UI, 1)
- withReason(REASON_ENROLL_ENROLLING, isDebuggable = true) {
- showUdfpsOverlay(isEnrollUseCase = false)
- }
- Settings.Global.putInt(mContext.contentResolver, SETTING_REMOVE_ENROLLMENT_UI, 0)
- }
-
- @Test
fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() }
private fun withRotation(@Rotation rotation: Int, block: () -> Unit) {
@@ -281,7 +242,7 @@
}
}
- private fun showUdfpsOverlay(isEnrollUseCase: Boolean = false) {
+ private fun showUdfpsOverlay() {
val didShow = controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(eq(controllerOverlay.overlayView), any())
@@ -293,12 +254,6 @@
assertThat(controllerOverlay.isShowing).isTrue()
assertThat(controllerOverlay.isHiding).isFalse()
assertThat(controllerOverlay.overlayView).isNotNull()
- if (isEnrollUseCase) {
- verify(udfpsEnrollView).updateSensorLocation(eq(overlayParams.sensorBounds))
- assertThat(controllerOverlay.enrollHelper).isNotNull()
- } else {
- assertThat(controllerOverlay.enrollHelper).isNull()
- }
}
@Test
@@ -311,12 +266,6 @@
fun hideUdfpsOverlay_settings() = withReason(REASON_AUTH_SETTINGS) { hideUdfpsOverlay() }
@Test
- fun hideUdfpsOverlay_locate() = withReason(REASON_ENROLL_FIND_SENSOR) { hideUdfpsOverlay() }
-
- @Test
- fun hideUdfpsOverlay_enroll() = withReason(REASON_ENROLL_ENROLLING) { hideUdfpsOverlay() }
-
- @Test
fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() }
private fun hideUdfpsOverlay() {
@@ -346,44 +295,6 @@
}
@Test
- fun forwardEnrollProgressEvents() = withReason(REASON_ENROLL_ENROLLING) {
- controllerOverlay.show(udfpsController, overlayParams)
-
- with(EnrollListener(controllerOverlay)) {
- controllerOverlay.onEnrollmentProgress(/* remaining */20)
- controllerOverlay.onAcquiredGood()
- assertThat(progress).isTrue()
- assertThat(help).isFalse()
- assertThat(acquired).isFalse()
- }
- }
-
- @Test
- fun forwardEnrollHelpEvents() = withReason(REASON_ENROLL_ENROLLING) {
- controllerOverlay.show(udfpsController, overlayParams)
-
- with(EnrollListener(controllerOverlay)) {
- controllerOverlay.onEnrollmentHelp()
- assertThat(progress).isFalse()
- assertThat(help).isTrue()
- assertThat(acquired).isFalse()
- }
- }
-
- @Test
- fun forwardEnrollAcquiredEvents() = withReason(REASON_ENROLL_ENROLLING) {
- controllerOverlay.show(udfpsController, overlayParams)
-
- with(EnrollListener(controllerOverlay)) {
- controllerOverlay.onEnrollmentProgress(/* remaining */ 1)
- controllerOverlay.onAcquiredGood()
- assertThat(progress).isTrue()
- assertThat(help).isFalse()
- assertThat(acquired).isTrue()
- }
- }
-
- @Test
fun cancels() = withReason(REASON_AUTH_BP) {
controllerOverlay.cancel()
verify(controllerCallback).onUserCanceled()
@@ -404,27 +315,3 @@
assertThat(controllerOverlay.matchesRequestId(REQUEST_ID + 1)).isFalse()
}
}
-
-private class EnrollListener(
- overlay: UdfpsControllerOverlay,
- var progress: Boolean = false,
- var help: Boolean = false,
- var acquired: Boolean = false
-) : UdfpsEnrollHelper.Listener {
-
- init {
- overlay.enrollHelper!!.setListener(this)
- }
-
- override fun onEnrollmentProgress(remaining: Int, totalSteps: Int) {
- progress = true
- }
-
- override fun onEnrollmentHelp(remaining: Int, totalSteps: Int) {
- help = true
- }
-
- override fun onLastStepAcquired() {
- acquired = true
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsEnrollViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsEnrollViewTest.java
deleted file mode 100644
index 60a0258..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsEnrollViewTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.res.Configuration;
-import android.graphics.Color;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class UdfpsEnrollViewTest extends SysuiTestCase {
-
- private static String ENROLL_PROGRESS_COLOR_LIGHT = "#699FF3";
- private static String ENROLL_PROGRESS_COLOR_DARK = "#7DA7F1";
-
- @Test
- public void fingerprintUdfpsEnroll_usesCorrectThemeCheckmarkFillColor() {
- final Configuration config = mContext.getResources().getConfiguration();
- final boolean isDarkThemeOn = (config.uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
- final int currentColor = mContext.getColor(R.color.udfps_enroll_progress);
-
- assertThat(currentColor).isEqualTo(Color.parseColor(isDarkThemeOn
- ? ENROLL_PROGRESS_COLOR_DARK : ENROLL_PROGRESS_COLOR_LIGHT));
- }
-}
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 fb54d6d..4415033 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -157,25 +157,28 @@
dumpManager = mock(),
userHandle = UserHandle.SYSTEM,
)
+ val featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
+ set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
+ set(Flags.REVAMPED_WALLPAPER_UI, true)
+ set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
underTest.interactor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
KeyguardInteractor(
repository = FakeKeyguardRepository(),
commandQueue = commandQueue,
+ featureFlags = featureFlags,
),
registry = mock(),
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
activityStarter = activityStarter,
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
- set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
- set(Flags.REVAMPED_WALLPAPER_UI, true)
- set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
- },
+ featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 68d13d3..d938243 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -18,30 +18,34 @@
package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager
+import android.content.Context
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
+import com.android.systemui.settings.DisplayTracker
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.CommandQueue.Callbacks
-import com.android.systemui.util.mockito.argumentCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.onCompletion
+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
import org.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.Mockito.verify
+import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(JUnit4::class)
class KeyguardInteractorTest : SysuiTestCase() {
- @Mock private lateinit var commandQueue: CommandQueue
+ private lateinit var commandQueue: FakeCommandQueue
+ private lateinit var featureFlags: FakeFeatureFlags
+ private lateinit var testScope: TestScope
private lateinit var underTest: KeyguardInteractor
private lateinit var repository: FakeKeyguardRepository
@@ -49,38 +53,134 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
+ featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
+ commandQueue = FakeCommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java))
+ testScope = TestScope()
repository = FakeKeyguardRepository()
- underTest = KeyguardInteractor(repository, commandQueue)
+ underTest = KeyguardInteractor(repository, commandQueue, featureFlags)
}
@Test
- fun onCameraLaunchDetected() = runTest {
- val flow = underTest.onCameraLaunchDetected
- var cameraLaunchSource = collectLastValue(flow)
- runCurrent()
+ fun onCameraLaunchDetected() =
+ testScope.runTest {
+ val flow = underTest.onCameraLaunchDetected
+ var cameraLaunchSource = collectLastValue(flow)
+ runCurrent()
- val captor = argumentCaptor<CommandQueue.Callbacks>()
- verify(commandQueue).addCallback(captor.capture())
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
+ }
+ assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE)
- captor.value.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
- assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE)
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+ )
+ }
+ assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP)
- captor.value.onCameraLaunchGestureDetected(
- StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
- )
- assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP)
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)
+ }
+ assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER)
- captor.value.onCameraLaunchGestureDetected(
- StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER
- )
- assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER)
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE
+ )
+ }
+ assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE)
- captor.value.onCameraLaunchGestureDetected(
- StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE
- )
- assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE)
+ flow.onCompletion { assertThat(commandQueue.callbackCount()).isEqualTo(0) }
+ }
- flow.onCompletion { verify(commandQueue).removeCallback(captor.value) }
+ @Test
+ fun testKeyguardGuardVisibilityStopsSecureCamera() =
+ testScope.runTest {
+ val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+ runCurrent()
+
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+ )
+ }
+
+ assertThat(secureCameraActive()).isTrue()
+
+ // Keyguard is showing but occluded
+ repository.setKeyguardShowing(true)
+ repository.setKeyguardOccluded(true)
+ assertThat(secureCameraActive()).isTrue()
+
+ // Keyguard is showing and not occluded
+ repository.setKeyguardOccluded(false)
+ assertThat(secureCameraActive()).isFalse()
+ }
+
+ @Test
+ fun testBouncerShowingResetsSecureCameraState() =
+ testScope.runTest {
+ val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+ runCurrent()
+
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+ )
+ }
+ assertThat(secureCameraActive()).isTrue()
+
+ // Keyguard is showing and not occluded
+ repository.setKeyguardShowing(true)
+ repository.setKeyguardOccluded(true)
+ assertThat(secureCameraActive()).isTrue()
+
+ repository.setBouncerShowing(true)
+ assertThat(secureCameraActive()).isFalse()
+ }
+
+ @Test
+ fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest {
+ var isVisible = collectLastValue(underTest.isKeyguardVisible)
+ repository.setKeyguardShowing(true)
+ repository.setKeyguardOccluded(false)
+
+ assertThat(isVisible()).isTrue()
+
+ repository.setKeyguardOccluded(true)
+ assertThat(isVisible()).isFalse()
+
+ repository.setKeyguardShowing(false)
+ repository.setKeyguardOccluded(true)
+ assertThat(isVisible()).isFalse()
}
+
+ @Test
+ fun secureCameraIsNotActiveWhenNoCameraLaunchEventHasBeenFiredYet() =
+ testScope.runTest {
+ val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+ runCurrent()
+
+ assertThat(secureCameraActive()).isFalse()
+ }
+}
+
+class FakeCommandQueue(val context: Context, val displayTracker: DisplayTracker) :
+ CommandQueue(context, displayTracker) {
+ private val callbacks = mutableListOf<Callbacks>()
+
+ override fun addCallback(callback: Callbacks) {
+ callbacks.add(callback)
+ }
+
+ override fun removeCallback(callback: Callbacks) {
+ callbacks.remove(callback)
+ }
+
+ fun doForEachCallback(func: (callback: Callbacks) -> Unit) {
+ callbacks.forEach { func(it) }
+ }
+
+ fun callbackCount(): Int = callbacks.size
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 43287b0..240af7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -286,12 +286,18 @@
dumpManager = mock(),
userHandle = UserHandle.SYSTEM,
)
+ val featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
KeyguardInteractor(
repository = FakeKeyguardRepository(),
- commandQueue = commandQueue
+ commandQueue = commandQueue,
+ featureFlags = featureFlags,
),
registry =
FakeKeyguardQuickAffordanceRegistry(
@@ -311,10 +317,7 @@
keyguardStateController = keyguardStateController,
userTracker = userTracker,
activityStarter = activityStarter,
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
- },
+ featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
)
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 b75a15d..8cff0ae 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
@@ -150,12 +150,17 @@
featureFlags =
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
}
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractor(repository = repository, commandQueue = commandQueue),
+ KeyguardInteractor(
+ repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags
+ ),
registry =
FakeKeyguardQuickAffordanceRegistry(
mapOf(
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 702f3763..2fd39b7 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
@@ -21,6 +21,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl
@@ -92,10 +94,12 @@
transitionRepository = KeyguardTransitionRepositoryImpl()
runner = KeyguardTransitionRunner(transitionRepository)
+ val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
fromLockscreenTransitionInteractor =
FromLockscreenTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
shadeRepository = shadeRepository,
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
@@ -105,7 +109,8 @@
fromDreamingTransitionInteractor =
FromDreamingTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -114,7 +119,8 @@
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -123,7 +129,8 @@
fromGoneTransitionInteractor =
FromGoneTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -132,7 +139,8 @@
fromDozingTransitionInteractor =
FromDozingTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -141,7 +149,8 @@
fromOccludedTransitionInteractor =
FromOccludedTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
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 4b04b7b..03a347e 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
@@ -125,9 +125,18 @@
),
)
repository = FakeKeyguardRepository()
+ val featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
val keyguardInteractor =
- KeyguardInteractor(repository = repository, commandQueue = commandQueue)
+ KeyguardInteractor(
+ repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags,
+ )
whenever(userTracker.userHandle).thenReturn(mock())
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
@@ -191,10 +200,7 @@
keyguardStateController = keyguardStateController,
userTracker = userTracker,
activityStarter = activityStarter,
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
- },
+ featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/process/condition/UserProcessConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/process/condition/UserProcessConditionTest.java
new file mode 100644
index 0000000..2293fc5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/process/condition/UserProcessConditionTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.process.condition;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.process.ProcessWrapper;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class UserProcessConditionTest extends SysuiTestCase {
+ @Mock
+ UserTracker mUserTracker;
+
+ @Mock
+ ProcessWrapper mProcessWrapper;
+
+ @Mock
+ Monitor.Callback mCallback;
+
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Verifies condition reports false when tracker reports a different user id than the
+ * identifier from the process handle.
+ */
+ @Test
+ public void testConditionFailsWithDifferentIds() {
+
+ final Condition condition = new UserProcessCondition(mProcessWrapper, mUserTracker);
+ when(mProcessWrapper.getUserHandleIdentifier()).thenReturn(0);
+ when(mUserTracker.getUserId()).thenReturn(1);
+
+ final Monitor monitor = new Monitor(mExecutor);
+
+ monitor.addSubscription(new Monitor.Subscription.Builder(mCallback)
+ .addCondition(condition)
+ .build());
+
+ mExecutor.runAllReady();
+
+ verify(mCallback).onConditionsChanged(false);
+ }
+
+ /**
+ * Verifies condition reports false when tracker reports a different user id than the
+ * identifier from the process handle.
+ */
+ @Test
+ public void testConditionSucceedsWithSameIds() {
+
+ final Condition condition = new UserProcessCondition(mProcessWrapper, mUserTracker);
+ when(mProcessWrapper.getUserHandleIdentifier()).thenReturn(0);
+ when(mUserTracker.getUserId()).thenReturn(0);
+
+ final Monitor monitor = new Monitor(mExecutor);
+
+ monitor.addSubscription(new Monitor.Subscription.Builder(mCallback)
+ .addCondition(condition)
+ .build());
+
+ mExecutor.runAllReady();
+
+ verify(mCallback).onConditionsChanged(true);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
index 7693fee..9eccbb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
@@ -471,4 +471,142 @@
mExecutor.runAllReady();
verify(callback).onConditionsChanged(true);
}
+
+ /**
+ * Ensures that the result of a condition being true leads to its nested condition being
+ * activated.
+ */
+ @Test
+ public void testNestedCondition() {
+ mCondition1.fakeUpdateCondition(false);
+ final Monitor.Callback callback =
+ mock(Monitor.Callback.class);
+
+ mCondition2.fakeUpdateCondition(false);
+
+ // Create a nested condition
+ mConditionMonitor.addSubscription(new Monitor.Subscription.Builder(
+ new Monitor.Subscription.Builder(callback)
+ .addCondition(mCondition2)
+ .build())
+ .addCondition(mCondition1)
+ .build());
+
+ mExecutor.runAllReady();
+
+ // Ensure the nested condition callback is not called at all.
+ verify(callback, never()).onActiveChanged(anyBoolean());
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+
+ // Update the inner condition to true and ensure that the nested condition is not triggered.
+ mCondition2.fakeUpdateCondition(true);
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+ mCondition2.fakeUpdateCondition(false);
+
+ // Set outer condition and make sure the inner condition becomes active and reports that
+ // conditions aren't met
+ mCondition1.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
+
+ verify(callback).onActiveChanged(eq(true));
+ verify(callback).onConditionsChanged(eq(false));
+
+ Mockito.clearInvocations(callback);
+
+ // Update the inner condition and make sure the callback is updated.
+ mCondition2.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
+
+ verify(callback).onConditionsChanged(true);
+
+ Mockito.clearInvocations(callback);
+ // Invalidate outer condition and make sure callback is informed, but the last state is
+ // not affected.
+ mCondition1.fakeUpdateCondition(false);
+ mExecutor.runAllReady();
+
+ verify(callback).onActiveChanged(eq(false));
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+ }
+
+ /**
+ * Ensures a subscription is predicated on its precondition.
+ */
+ @Test
+ public void testPrecondition() {
+ mCondition1.fakeUpdateCondition(false);
+ final Monitor.Callback callback =
+ mock(Monitor.Callback.class);
+
+ mCondition2.fakeUpdateCondition(false);
+
+ // Create a nested condition
+ mConditionMonitor.addSubscription(new Monitor.Subscription.Builder(callback)
+ .addPrecondition(mCondition1)
+ .addCondition(mCondition2)
+ .build());
+
+ mExecutor.runAllReady();
+
+ // Ensure the nested condition callback is not called at all.
+ verify(callback, never()).onActiveChanged(anyBoolean());
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+
+ // Update the condition to true and ensure that the nested condition is not triggered.
+ mCondition2.fakeUpdateCondition(true);
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+ mCondition2.fakeUpdateCondition(false);
+
+ // Set precondition and make sure the inner condition becomes active and reports that
+ // conditions aren't met
+ mCondition1.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
+
+ verify(callback).onActiveChanged(eq(true));
+ verify(callback).onConditionsChanged(eq(false));
+
+ Mockito.clearInvocations(callback);
+
+ // Update the condition and make sure the callback is updated.
+ mCondition2.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
+
+ verify(callback).onConditionsChanged(true);
+
+ Mockito.clearInvocations(callback);
+ // Invalidate precondition and make sure callback is informed, but the last state is
+ // not affected.
+ mCondition1.fakeUpdateCondition(false);
+ mExecutor.runAllReady();
+
+ verify(callback).onActiveChanged(eq(false));
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+ }
+
+ /**
+ * Ensure preconditions are applied to every subscription added to a monitor.
+ */
+ @Test
+ public void testPreconditionMonitor() {
+ final Monitor.Callback callback =
+ mock(Monitor.Callback.class);
+
+ mCondition2.fakeUpdateCondition(true);
+ final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(mCondition1)));
+
+ monitor.addSubscription(new Monitor.Subscription.Builder(callback)
+ .addCondition(mCondition2)
+ .build());
+
+ mExecutor.runAllReady();
+
+ verify(callback, never()).onActiveChanged(anyBoolean());
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+
+ mCondition1.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
+
+ verify(callback).onActiveChanged(eq(true));
+ verify(callback).onConditionsChanged(eq(true));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
index 756397a..74ed7fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
@@ -42,13 +42,35 @@
pixelDensity = 2f,
color = Color.RED,
opacity = 30,
- shouldFillRipple = true,
+ baseRingFadeParams =
+ RippleShader.FadeParams(
+ fadeInStart = 0f,
+ fadeInEnd = 0.3f,
+ fadeOutStart = 0.5f,
+ fadeOutEnd = 1f
+ ),
+ sparkleRingFadeParams =
+ RippleShader.FadeParams(
+ fadeInStart = 0.1f,
+ fadeInEnd = 0.2f,
+ fadeOutStart = 0.7f,
+ fadeOutEnd = 0.9f
+ ),
+ centerFillFadeParams =
+ RippleShader.FadeParams(
+ fadeInStart = 0f,
+ fadeInEnd = 0.1f,
+ fadeOutStart = 0.2f,
+ fadeOutEnd = 0.3f
+ ),
sparkleStrength = 0.3f
)
val rippleAnimation = RippleAnimation(config)
with(rippleAnimation.rippleShader) {
- assertThat(rippleFill).isEqualTo(config.shouldFillRipple)
+ assertThat(baseRingFadeParams).isEqualTo(config.baseRingFadeParams)
+ assertThat(sparkleRingFadeParams).isEqualTo(config.sparkleRingFadeParams)
+ assertThat(centerFillFadeParams).isEqualTo(config.centerFillFadeParams)
assertThat(pixelDensity).isEqualTo(config.pixelDensity)
assertThat(color).isEqualTo(ColorUtils.setAlphaComponent(config.color, config.opacity))
assertThat(sparkleStrength).isEqualTo(config.sparkleStrength)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index f4226bc..3d75967 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -25,6 +25,8 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -105,8 +107,13 @@
}
keyguardRepository = FakeKeyguardRepository()
+ val featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
val keyguardInteractor =
- KeyguardInteractor(repository = keyguardRepository, commandQueue = commandQueue)
+ KeyguardInteractor(
+ repository = keyguardRepository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags
+ )
// Needs to be run on the main thread
runBlocking(IMMEDIATE) {
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 7380ca4..3538d9b 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
@@ -119,8 +119,11 @@
SUPERVISED_USER_CREATION_APP_PACKAGE,
)
- featureFlags = FakeFeatureFlags()
- featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
userRepository = FakeUserRepository()
keyguardRepository = FakeKeyguardRepository()
telephonyRepository = FakeTelephonyRepository()
@@ -141,6 +144,7 @@
KeyguardInteractor(
repository = keyguardRepository,
commandQueue = commandQueue,
+ featureFlags = featureFlags,
),
manager = manager,
applicationScope = testScope.backgroundScope,
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 46f38ed..8f0375f 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
@@ -82,7 +82,6 @@
private val userRepository = FakeUserRepository()
private val keyguardRepository = FakeKeyguardRepository()
- private val featureFlags = FakeFeatureFlags()
private lateinit var guestUserInteractor: GuestUserInteractor
private lateinit var refreshUsersScheduler: RefreshUsersScheduler
@@ -233,6 +232,11 @@
}
private fun viewModel(): StatusBarUserChipViewModel {
+ val featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
return StatusBarUserChipViewModel(
context = context,
interactor =
@@ -244,9 +248,9 @@
KeyguardInteractor(
repository = keyguardRepository,
commandQueue = commandQueue,
+ featureFlags = featureFlags,
),
- featureFlags =
- FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) },
+ featureFlags = featureFlags,
manager = manager,
applicationScope = testScope.backgroundScope,
telephonyInteractor =
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 1d57d0b..71a112c 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
@@ -134,6 +134,11 @@
resetOrExitSessionReceiver = resetOrExitSessionReceiver,
)
+ val featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
underTest =
UserSwitcherViewModel.Factory(
userInteractor =
@@ -145,11 +150,9 @@
KeyguardInteractor(
repository = keyguardRepository,
commandQueue = commandQueue,
+ featureFlags = featureFlags
),
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- },
+ featureFlags = featureFlags,
manager = manager,
applicationScope = testScope.backgroundScope,
telephonyInteractor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
new file mode 100644
index 0000000..5ef62c1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.util.condition;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ConditionalCoreStartableTest extends SysuiTestCase {
+ public static class FakeConditionalCoreStartable extends ConditionalCoreStartable {
+ interface Callback {
+ void onStart();
+ void bootCompleted();
+ }
+
+ private final Callback mCallback;
+
+ public FakeConditionalCoreStartable(Monitor monitor, Set<Condition> conditions,
+ Callback callback) {
+ super(monitor, conditions);
+ mCallback = callback;
+ }
+
+ @Override
+ protected void onStart() {
+ mCallback.onStart();
+ }
+
+ @Override
+ protected void bootCompleted() {
+ mCallback.bootCompleted();
+ }
+ }
+
+
+ final Set<Condition> mConditions = new HashSet<>();
+
+ @Mock
+ Condition mCondition;
+
+ @Mock
+ Monitor mMonitor;
+
+ @Mock
+ FakeConditionalCoreStartable.Callback mCallback;
+
+ @Mock
+ Monitor.Subscription.Token mSubscriptionToken;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mConditions.clear();
+ }
+
+ /**
+ * Verifies that {@link ConditionalCoreStartable#onStart()} is predicated on conditions being
+ * met.
+ */
+ @Test
+ public void testOnStartCallback() {
+ final CoreStartable coreStartable =
+ new FakeConditionalCoreStartable(mMonitor,
+ new HashSet<>(Arrays.asList(mCondition)),
+ mCallback);
+
+ when(mMonitor.addSubscription(any())).thenReturn(mSubscriptionToken);
+ coreStartable.start();
+
+ final ArgumentCaptor<Monitor.Subscription> subscriptionCaptor = ArgumentCaptor.forClass(
+ Monitor.Subscription.class);
+ verify(mMonitor).addSubscription(subscriptionCaptor.capture());
+
+ final Monitor.Subscription subscription = subscriptionCaptor.getValue();
+
+ assertThat(subscription.getConditions()).containsExactly(mCondition);
+
+ verify(mCallback, never()).onStart();
+
+ subscription.getCallback().onConditionsChanged(true);
+
+ verify(mCallback).onStart();
+ verify(mMonitor).removeSubscription(mSubscriptionToken);
+ }
+
+
+ /**
+ * Verifies that {@link ConditionalCoreStartable#bootCompleted()} ()} is predicated on
+ * conditions being met.
+ */
+ @Test
+ public void testBootCompleted() {
+ final CoreStartable coreStartable =
+ new FakeConditionalCoreStartable(mMonitor,
+ new HashSet<>(Arrays.asList(mCondition)),
+ mCallback);
+
+ when(mMonitor.addSubscription(any())).thenReturn(mSubscriptionToken);
+ coreStartable.onBootCompleted();
+
+ final ArgumentCaptor<Monitor.Subscription> subscriptionCaptor = ArgumentCaptor.forClass(
+ Monitor.Subscription.class);
+ verify(mMonitor).addSubscription(subscriptionCaptor.capture());
+
+ final Monitor.Subscription subscription = subscriptionCaptor.getValue();
+
+ assertThat(subscription.getConditions()).containsExactly(mCondition);
+
+ verify(mCallback, never()).bootCompleted();
+
+ subscription.getCallback().onConditionsChanged(true);
+
+ verify(mCallback).bootCompleted();
+ verify(mMonitor).removeSubscription(mSubscriptionToken);
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 776405d..7e4567b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2725,6 +2725,7 @@
somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
somethingChanged |= readMagnificationCapabilitiesLocked(userState);
somethingChanged |= readMagnificationFollowTypingLocked(userState);
+ somethingChanged |= readAlwaysOnMagnificationLocked(userState);
somethingChanged |= readUiContrastLocked(userState);
return somethingChanged;
}
@@ -4378,6 +4379,10 @@
private final Uri mMagnificationFollowTypingUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
+ // TODO: replace name with Settings Secure Key
+ private final Uri mAlwaysOnMagnificationUri = Settings.Secure.getUriFor(
+ "accessibility_magnification_always_on_enabled");
+
private final Uri mUiContrastUri = Settings.Secure.getUriFor(
CONTRAST_LEVEL);
@@ -4422,6 +4427,8 @@
contentResolver.registerContentObserver(
mMagnificationFollowTypingUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
+ mAlwaysOnMagnificationUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
mUiContrastUri, false, this, UserHandle.USER_ALL);
}
@@ -4492,6 +4499,8 @@
}
} else if (mMagnificationFollowTypingUri.equals(uri)) {
readMagnificationFollowTypingLocked(userState);
+ } else if (mAlwaysOnMagnificationUri.equals(uri)) {
+ readAlwaysOnMagnificationLocked(userState);
} else if (mUiContrastUri.equals(uri)) {
if (readUiContrastLocked(userState)) {
updateUiContrastLocked(userState);
@@ -4605,6 +4614,23 @@
return false;
}
+ boolean readAlwaysOnMagnificationLocked(AccessibilityUserState userState) {
+ // TODO: replace name const with Settings Secure Key
+ final boolean isSettingsAlwaysOnEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ "accessibility_magnification_always_on_enabled",
+ 0, userState.mUserId) == 1;
+ final boolean isAlwaysOnFeatureFlagEnabled = mMagnificationController
+ .isAlwaysOnMagnificationFeatureFlagEnabled();
+ final boolean isAlwaysOnEnabled = isAlwaysOnFeatureFlagEnabled && isSettingsAlwaysOnEnabled;
+ if (isAlwaysOnEnabled != userState.isAlwaysOnMagnificationEnabled()) {
+ userState.setAlwaysOnMagnificationEnabled(isAlwaysOnEnabled);
+ mMagnificationController.setAlwaysOnMagnificationEnabled(isAlwaysOnEnabled);
+ return true;
+ }
+ return false;
+ }
+
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
mMainHandler.sendMessage(
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 43730fc..1c9ce3c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -136,6 +136,8 @@
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
// Whether the following typing focus feature for magnification is enabled.
private boolean mMagnificationFollowTypingEnabled = true;
+ // Whether the always on magnification feature is enabled.
+ private boolean mAlwaysOnMagnificationEnabled = false;
/** The stroke width of the focus rectangle in pixels */
private int mFocusStrokeWidth;
@@ -221,6 +223,7 @@
mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
mFocusColor = mFocusColorDefaultValue;
mMagnificationFollowTypingEnabled = true;
+ mAlwaysOnMagnificationEnabled = false;
mUiContrast = CONTRAST_NOT_SET;
}
@@ -531,6 +534,8 @@
.append(String.valueOf(mIsAudioDescriptionByDefaultRequested));
pw.append(", magnificationFollowTypingEnabled=")
.append(String.valueOf(mMagnificationFollowTypingEnabled));
+ pw.append(", alwaysOnMagnificationEnabled=")
+ .append(String.valueOf(mAlwaysOnMagnificationEnabled));
pw.append("}");
pw.println();
pw.append(" shortcut key:{");
@@ -711,6 +716,14 @@
return mMagnificationFollowTypingEnabled;
}
+ public void setAlwaysOnMagnificationEnabled(boolean enabled) {
+ mAlwaysOnMagnificationEnabled = enabled;
+ }
+
+ public boolean isAlwaysOnMagnificationEnabled() {
+ return mAlwaysOnMagnificationEnabled;
+ }
+
/**
* Sets the magnification mode to the given display.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 595cdec..d7c5b5d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -57,6 +57,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.wm.WindowManagerInternal;
@@ -99,7 +100,8 @@
private final Rect mTempRect = new Rect();
// Whether the following typing focus feature for magnification is enabled.
private boolean mMagnificationFollowTypingEnabled = true;
-
+ // Whether the always on magnification feature is enabled.
+ private boolean mAlwaysOnMagnificationEnabled = false;
private final DisplayManagerInternal mDisplayManagerInternal;
/**
@@ -291,18 +293,15 @@
@Override
public void onDisplaySizeChanged() {
- // Treat as context change and reset
- final Message m = PooledLambda.obtainMessage(
- FullScreenMagnificationController::resetIfNeeded,
- FullScreenMagnificationController.this, mDisplayId, true);
- mControllerCtx.getHandler().sendMessage(m);
+ // Treat as context change
+ onUserContextChanged();
}
@Override
public void onUserContextChanged() {
final Message m = PooledLambda.obtainMessage(
- FullScreenMagnificationController::resetIfNeeded,
- FullScreenMagnificationController.this, mDisplayId, true);
+ FullScreenMagnificationController::onUserContextChanged,
+ FullScreenMagnificationController.this, mDisplayId);
mControllerCtx.getHandler().sendMessage(m);
}
@@ -795,6 +794,33 @@
return mMagnificationFollowTypingEnabled;
}
+ void setAlwaysOnMagnificationEnabled(boolean enabled) {
+ mAlwaysOnMagnificationEnabled = enabled;
+ }
+
+ boolean isAlwaysOnMagnificationEnabled() {
+ return mAlwaysOnMagnificationEnabled;
+ }
+
+ /**
+ * if the magnifier with given displayId is activated:
+ * 1. if {@link #isAlwaysOnMagnificationEnabled()}, zoom the magnifier to 100%,
+ * 2. otherwise, reset the magnification.
+ *
+ * @param displayId The logical display id.
+ */
+ void onUserContextChanged(int displayId) {
+ synchronized (mLock) {
+ if (isAlwaysOnMagnificationEnabled()) {
+ setScaleAndCenter(displayId, 1.0f, Float.NaN, Float.NaN,
+ true,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ } else {
+ reset(displayId, true);
+ }
+ }
+ }
+
/**
* Remove the display magnification with given id.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 9fc9d57..9c84c04 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -445,12 +445,12 @@
*/
final class ViewportDraggingState implements State {
- /** Whether to disable zoom after dragging ends */
- @VisibleForTesting boolean mActivatedBeforeDrag;
- /** Whether to restore scale after dragging ends */
- private boolean mZoomedInTemporary;
- /** The cached scale for recovering after dragging ends */
- private float mScaleBeforeZoomedInTemporary;
+ /**
+ * The cached scale for recovering after dragging ends.
+ * If the scale >= 1.0, the magnifier needs to recover to scale.
+ * Otherwise, the magnifier should be disabled.
+ */
+ @VisibleForTesting float mScaleToRecoverAfterDraggingEnd = Float.NaN;
private boolean mLastMoveOutsideMagnifiedRegion;
@@ -460,8 +460,7 @@
final int action = event.getActionMasked();
switch (action) {
case ACTION_POINTER_DOWN: {
- clear();
- transitionTo(mPanningScalingState);
+ clearAndTransitToPanningScalingState();
}
break;
case ACTION_MOVE: {
@@ -484,14 +483,18 @@
case ACTION_UP:
case ACTION_CANCEL: {
- if (mActivatedBeforeDrag) {
- if (mZoomedInTemporary) {
- zoomToScale(mScaleBeforeZoomedInTemporary, event.getX(), event.getY());
- }
+ // If mScaleToRecoverAfterDraggingEnd >= 1.0, the dragging state is triggered
+ // by zoom in temporary, and the magnifier needs to recover to original scale
+ // after exiting dragging state.
+ // Otherwise, the magnifier should be disabled.
+ if (mScaleToRecoverAfterDraggingEnd >= 1.0f) {
+ zoomToScale(mScaleToRecoverAfterDraggingEnd, event.getX(),
+ event.getY());
} else {
zoomOff();
}
clear();
+ mScaleToRecoverAfterDraggingEnd = Float.NaN;
transitionTo(mDetectingState);
}
break;
@@ -504,27 +507,49 @@
}
}
- public void prepareForZoomInTemporary() {
- mViewportDraggingState.mActivatedBeforeDrag =
- mFullScreenMagnificationController.isActivated(mDisplayId);
+ private boolean isAlwaysOnMagnificationEnabled() {
+ return mFullScreenMagnificationController.isAlwaysOnMagnificationEnabled();
+ }
- mViewportDraggingState.mZoomedInTemporary = true;
- mViewportDraggingState.mScaleBeforeZoomedInTemporary =
- mFullScreenMagnificationController.getScale(mDisplayId);
+ public void prepareForZoomInTemporary(boolean shortcutTriggered) {
+ boolean shouldRecoverAfterDraggingEnd;
+ if (mFullScreenMagnificationController.isActivated(mDisplayId)) {
+ // For b/267210808, if always-on feature is not enabled, we keep the expected
+ // behavior. If users tap shortcut and then tap-and-hold to zoom in temporary,
+ // the magnifier should be disabled after release.
+ // If always-on feature is enabled, in the same scenario the magnifier would
+ // zoom to 1.0 and keep activated.
+ if (shortcutTriggered) {
+ shouldRecoverAfterDraggingEnd = isAlwaysOnMagnificationEnabled();
+ } else {
+ shouldRecoverAfterDraggingEnd = true;
+ }
+ } else {
+ shouldRecoverAfterDraggingEnd = false;
+ }
+
+ mScaleToRecoverAfterDraggingEnd = shouldRecoverAfterDraggingEnd
+ ? mFullScreenMagnificationController.getScale(mDisplayId) : Float.NaN;
+ }
+
+ private void clearAndTransitToPanningScalingState() {
+ final float scaleToRecovery = mScaleToRecoverAfterDraggingEnd;
+ clear();
+ mScaleToRecoverAfterDraggingEnd = scaleToRecovery;
+ transitionTo(mPanningScalingState);
}
@Override
public void clear() {
mLastMoveOutsideMagnifiedRegion = false;
- mZoomedInTemporary = false;
- mScaleBeforeZoomedInTemporary = 1.0f;
+ mScaleToRecoverAfterDraggingEnd = Float.NaN;
}
@Override
public String toString() {
return "ViewportDraggingState{"
- + "mActivatedBeforeDrag=" + mActivatedBeforeDrag
+ + "mScaleToRecoverAfterDraggingEnd=" + mScaleToRecoverAfterDraggingEnd
+ ", mLastMoveOutsideMagnifiedRegion=" + mLastMoveOutsideMagnifiedRegion
+ '}';
}
@@ -921,13 +946,14 @@
void transitionToViewportDraggingStateAndClear(MotionEvent down) {
if (DEBUG_DETECTING) Slog.i(mLogTag, "onTripleTapAndHold()");
+ final boolean shortcutTriggered = mShortcutTriggered;
clear();
// Triple tap and hold also belongs to triple tap event.
final boolean enabled = !isActivated();
logMagnificationTripleTap(enabled);
- mViewportDraggingState.prepareForZoomInTemporary();
+ mViewportDraggingState.prepareForZoomInTemporary(shortcutTriggered);
zoomInTemporary(down.getX(), down.getY());
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 558c71b..a6e6bd7 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -677,6 +677,19 @@
getFullScreenMagnificationController().setMagnificationFollowTypingEnabled(enabled);
}
+ /**
+ * Called when the always on magnification feature is switched.
+ *
+ * @param enabled Enable the always on magnification feature
+ */
+ public void setAlwaysOnMagnificationEnabled(boolean enabled) {
+ getFullScreenMagnificationController().setAlwaysOnMagnificationEnabled(enabled);
+ }
+
+ public boolean isAlwaysOnMagnificationFeatureFlagEnabled() {
+ return AlwaysOnMagnificationFeatureFlag.isAlwaysOnMagnificationEnabled();
+ }
+
private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
int displayId) {
return mMagnificationEndRunnableSparseArray.get(displayId);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 7df4899..e86d997 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1028,8 +1028,9 @@
intent.setComponent(provider.getInfoLocked(mContext).configure);
intent.setFlags(secureFlags);
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setIgnorePendingIntentCreatorForegroundState(true);
+ final ActivityOptions options =
+ ActivityOptions.makeBasic().setPendingIntentCreatorBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED);
// All right, create the sender.
final long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index fc81675..eafd3e0 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -584,7 +584,7 @@
+ packageInfo.applicationInfo.sourceDir);
if (packageInfo.applicationInfo.sourceDir.startsWith("/data/apex/")) {
String origPackageFilepath = getOriginalApexPreinstalledLocation(
- packageInfo.packageName, packageInfo.applicationInfo.sourceDir);
+ packageInfo.packageName);
pw.println("|--> Pre-installed package install location: "
+ origPackageFilepath);
@@ -1628,8 +1628,7 @@
}
@NonNull
- private String getOriginalApexPreinstalledLocation(String packageName,
- String currentInstalledLocation) {
+ private String getOriginalApexPreinstalledLocation(String packageName) {
try {
final String moduleName = apexPackageNameToModuleName(packageName);
IApexService apexService = IApexService.Stub.asInterface(
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 6435869..653c5d1 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3380,8 +3380,9 @@
appInfo.manageSpaceActivityName);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setIgnorePendingIntentCreatorForegroundState(true);
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED);
PendingIntent activity = PendingIntent.getActivity(targetAppContext, requestCode,
intent,
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 5cdcd42..ef15beb 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -38,6 +38,8 @@
per-file ContentProviderHelper.java = varunshah@google.com, omakoto@google.com, jsharkey@google.com, yamasani@google.com
+per-file CachedAppOptimizer.java = file:/PERFORMANCE_OWNERS
+
# Multiuser
per-file User* = file:/MULTIUSER_OWNERS
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index bc90c89..add4a89 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -12084,11 +12084,16 @@
final SparseDoubleArray uidEstimatedConsumptionMah;
final long dataConsumedChargeUC;
if (consumedChargeUC > 0 && isMobileRadioEnergyConsumerSupportedLocked()) {
- // Crudely attribute power consumption. Added (totalRadioDurationMs / 2) to the
- // numerator for long rounding.
- final long phoneConsumedChargeUC =
- (consumedChargeUC * phoneOnDurationMs + totalRadioDurationMs / 2)
- / totalRadioDurationMs;
+ final long phoneConsumedChargeUC;
+ if (totalRadioDurationMs == 0) {
+ phoneConsumedChargeUC = 0;
+ } else {
+ // Crudely attribute power consumption. Added (totalRadioDurationMs / 2) to the
+ // numerator for long rounding.
+ phoneConsumedChargeUC =
+ (consumedChargeUC * phoneOnDurationMs + totalRadioDurationMs / 2)
+ / totalRadioDurationMs;
+ }
dataConsumedChargeUC = consumedChargeUC - phoneConsumedChargeUC;
mGlobalEnergyConsumerStats.updateStandardBucket(
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
index 3226260..aba8e5f 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
@@ -264,8 +264,10 @@
if (total.remainingPowerMah < 0) total.remainingPowerMah = 0;
} else {
// Smear unattributed active time and add it to the remaining power consumption.
- total.remainingPowerMah +=
- (totalActivePowerMah * total.remainingDurationMs) / totalActiveDurationMs;
+ if (totalActiveDurationMs != 0) {
+ total.remainingPowerMah +=
+ (totalActivePowerMah * total.remainingDurationMs) / totalActiveDurationMs;
+ }
// Calculate the inactive modem power consumption.
final BatteryStats.ControllerActivityCounter modemActivity =
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index a7883cb..5e066fa 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -605,11 +605,16 @@
final Task task = r.getTask();
mService.deferWindowLayout();
try {
- r.mTransitionController.requestStartTransition(transition,
- task, remoteTransition, null /* displayChange */);
- r.mTransitionController.collect(task);
- r.mTransitionController.setTransientLaunch(r,
- TaskDisplayArea.getRootTaskAbove(rootTask));
+ final TransitionController controller = r.mTransitionController;
+ if (controller.getTransitionPlayer() != null) {
+ controller.requestStartTransition(transition, task, remoteTransition,
+ null /* displayChange */);
+ controller.collect(task);
+ controller.setTransientLaunch(r, TaskDisplayArea.getRootTaskAbove(rootTask));
+ } else {
+ // The transition player might be died when executing the queued transition.
+ transition.abort();
+ }
task.moveToFront("startExistingRecents");
task.mInResumeTopActivity = true;
task.resumeTopActivity(null /* prev */, options, true /* deferPause */);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 0f1f51f..f34dc94 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2053,7 +2053,7 @@
// Stop any activities that are scheduled to do so but have been waiting for the transition
// animation to finish.
ArrayList<ActivityRecord> readyToStopActivities = null;
- for (int i = mStoppingActivities.size() - 1; i >= 0; --i) {
+ for (int i = 0; i < mStoppingActivities.size(); i++) {
final ActivityRecord s = mStoppingActivities.get(i);
final boolean animating = s.isInTransition();
ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b "
@@ -2074,6 +2074,7 @@
readyToStopActivities.add(s);
mStoppingActivities.remove(i);
+ i--;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9060456..ca3cfaf 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3952,23 +3952,6 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- // TODO: This should probably be called any time a visual change is made to the hierarchy like
- // moving containers or resizing them. Need to investigate the best way to have it automatically
- // happen so we don't run into issues with programmers forgetting to do it.
- void layoutAndAssignWindowLayersIfNeeded() {
- mWmService.mWindowsChanged = true;
- setLayoutNeeded();
-
- if (!mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/)) {
- assignWindowLayers(false /* setLayoutNeeded */);
- }
-
- mInputMonitor.setUpdateInputWindowsNeededLw();
- mWmService.mWindowPlacerLocked.performSurfacePlacement();
- mInputMonitor.updateInputWindowsLw(false /*force*/);
- }
-
/** Returns true if a leaked surface was destroyed */
boolean destroyLeakedSurfaces() {
// Used to indicate that a surface was leaked.
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 7071aa7..c081725 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -635,7 +635,8 @@
if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
if (!mDisplayContent.mTransitionController.isCollecting()) {
- throw new IllegalStateException("Trying to rotate outside a transition");
+ // The remote may be too slow to response before transition timeout.
+ Slog.e(TAG, "Trying to continue rotation outside a transition");
}
mDisplayContent.mTransitionController.collect(mDisplayContent);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1e53cc3..986c8f4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5873,9 +5873,6 @@
}
positionChildAt(POSITION_TOP, child, true /* includingParents */);
-
- final DisplayContent displayContent = getDisplayContent();
- displayContent.layoutAndAssignWindowLayersIfNeeded();
}
void positionChildAtBottom(Task child) {
@@ -5896,7 +5893,6 @@
}
positionChildAt(POSITION_BOTTOM, child, includingParents);
- getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
}
@Override
@@ -5910,12 +5906,6 @@
// Non-root task position changed.
mRootWindowContainer.invalidateTaskLayers();
}
-
- final boolean isTop = getTopChild() == child;
- if (isTop) {
- final DisplayContent displayContent = getDisplayContent();
- displayContent.layoutAndAssignWindowLayersIfNeeded();
- }
}
void reparent(TaskDisplayArea newParent, boolean onTop) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 0457408..a0608db 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -405,8 +405,6 @@
child.updateTaskMovement(moveToTop, targetPosition);
- mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
-
// The insert position may be adjusted to non-top when there is always-on-top root task.
// Since the original position is preferred to be top, the root task should have higher
// priority when we are looking for top focusable root task. The condition {@code
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index e1a144a..71bb99c 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -427,7 +427,10 @@
if (mState < STATE_COLLECTING) {
throw new IllegalStateException("Can't start Transition which isn't collecting.");
} else if (mState >= STATE_STARTED) {
- Slog.w(TAG, "Transition already started: " + mSyncId);
+ Slog.w(TAG, "Transition already started id=" + mSyncId + " state=" + mState);
+ // The transition may be aborted (STATE_ABORT) or timed out (STATE_PLAYING by
+ // SyncGroup#finishNow), so do not revert the state to STATE_STARTED.
+ return;
}
mState = STATE_STARTED;
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d",
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 7173980..eec9973 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -566,7 +566,6 @@
onDisplayChanged(dc);
prevDc.setLayoutNeeded();
}
- getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
// Send onParentChanged notification here is we disabled sending it in setParent for
// reparenting case.
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 296b013..3faf9e0 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -52,7 +52,6 @@
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
-import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
@@ -617,8 +616,8 @@
}
}
- if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
- mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
+ if (effects != 0) {
+ mService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
} finally {
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 669fdb5..6857239 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -341,11 +341,12 @@
IGetCredentialCallback callback,
final String callingPackage) {
Log.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
- // TODO : Implement cancellation
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
- int userId = UserHandle.getCallingUserId();
- int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
+ enforceCallingPackage(callingPackage, callingUid);
+
// New request session, scoped for this request only.
final GetRequestSession session =
new GetRequestSession(
@@ -446,13 +447,14 @@
CreateCredentialRequest request,
ICreateCredentialCallback callback,
String callingPackage) {
- Log.i(TAG, "starting executeCreateCredential with callingPackage: " + callingPackage);
-
+ Log.i(TAG, "starting executeCreateCredential with callingPackage: "
+ + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+ final int userId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
+ enforceCallingPackage(callingPackage, callingUid);
// New request session, scoped for this request only.
- int userId = UserHandle.getCallingUserId();
- int callingUid = Binder.getCallingUid();
final CreateRequestSession session =
new CreateRequestSession(
getContext(),
@@ -581,6 +583,8 @@
// TODO(253157366): Check additional set of services.
final int userId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
+ enforceCallingPackage(callingPackage, callingUid);
synchronized (mLock) {
final List<CredentialManagerServiceImpl> services =
getServiceListForUserLocked(userId);
@@ -611,12 +615,14 @@
IClearCredentialStateCallback callback,
String callingPackage) {
Log.i(TAG, "starting clearCredentialState with callingPackage: " + callingPackage);
+ final int userId = UserHandle.getCallingUserId();
+ int callingUid = Binder.getCallingUid();
+ enforceCallingPackage(callingPackage, callingUid);
+
// TODO : Implement cancellation
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
// New request session, scoped for this request only.
- int userId = UserHandle.getCallingUserId();
- int callingUid = Binder.getCallingUid();
final ClearRequestSession session =
new ClearRequestSession(
getContext(),
@@ -655,6 +661,8 @@
throws IllegalArgumentException, NonCredentialProviderCallerException {
Log.i(TAG, "registerCredentialDescription");
+ enforceCallingPackage(callingPackage, Binder.getCallingUid());
+
List<CredentialProviderInfo> services =
CredentialProviderInfo.getAvailableServices(
mContext, UserHandle.getCallingUserId());
@@ -705,7 +713,8 @@
UnregisterCredentialDescriptionRequest request, String callingPackage)
throws IllegalArgumentException {
Log.i(TAG, "registerCredentialDescription");
- ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+
+ enforceCallingPackage(callingPackage, Binder.getCallingUid());
List<CredentialProviderInfo> services =
CredentialProviderInfo.getAvailableServices(
@@ -728,4 +737,19 @@
session.executeUnregisterRequest(request, callingPackage);
}
}
+
+ private void enforceCallingPackage(String callingPackage, int callingUid) {
+ int packageUid;
+ PackageManager pm = mContext.createContextAsUser(
+ UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
+ try {
+ packageUid = pm.getPackageUid(callingPackage,
+ PackageManager.PackageInfoFlags.of(0));
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new SecurityException(callingPackage + " not found");
+ }
+ if (packageUid != callingUid) {
+ throw new SecurityException(callingPackage + " does not belong to uid " + callingUid);
+ }
+ }
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index ec983df..9033abc 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -229,7 +229,7 @@
// Populate the save entries
for (CreateEntry createEntry : saveEntries) {
- String entryId = generateEntryId();
+ String entryId = generateUniqueId();
mUiSaveEntries.put(entryId, createEntry);
Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
uiSaveEntries.add(new Entry(SAVE_ENTRY_KEY, entryId, createEntry.getSlice(),
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 1583b83..496a6d1 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -45,7 +45,6 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
-import java.util.stream.Collectors;
/**
* Central provider session that listens for provider callbacks, and maintains provider state.
@@ -68,6 +67,8 @@
private static final String CREDENTIAL_ENTRY_KEY = "credential_key";
@NonNull
+ private final Map<String, CredentialOption> mBeginGetOptionToCredentialOptionMap;
+ @NonNull
private final Map<String, CredentialEntry> mUiCredentialEntries = new HashMap<>();
@NonNull
private final Map<String, Action> mUiActionsEntries = new HashMap<>();
@@ -91,12 +92,16 @@
filterOptions(providerInfo.getCapabilities(),
getRequestSession.mClientRequest);
if (filteredRequest != null) {
+ Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
+ new HashMap<>();
BeginGetCredentialRequest beginGetCredentialRequest = constructQueryPhaseRequest(
filteredRequest, getRequestSession.mClientAppInfo,
- getRequestSession.mClientRequest.alwaysSendAppInfoToProvider());
+ getRequestSession.mClientRequest.alwaysSendAppInfoToProvider(),
+ beginGetOptionToCredentialOptionMap);
return new ProviderGetSession(context, providerInfo, getRequestSession, userId,
remoteCredentialService, beginGetCredentialRequest, filteredRequest,
- getRequestSession.mClientAppInfo);
+ getRequestSession.mClientAppInfo,
+ beginGetOptionToCredentialOptionMap);
}
Log.i(TAG, "Unable to create provider session");
return null;
@@ -105,15 +110,18 @@
private static BeginGetCredentialRequest constructQueryPhaseRequest(
android.credentials.GetCredentialRequest filteredRequest,
CallingAppInfo callingAppInfo,
- boolean propagateToProvider) {
+ boolean propagateToProvider,
+ Map<String, CredentialOption> beginGetOptionToCredentialOptionMap
+ ) {
BeginGetCredentialRequest.Builder builder = new BeginGetCredentialRequest.Builder();
- builder.setBeginGetCredentialOptions(
- filteredRequest.getCredentialOptions().stream().map(
- option -> {
- return new BeginGetCredentialOption(
- option.getType(),
- option.getCandidateQueryData());
- }).collect(Collectors.toList()));
+ filteredRequest.getCredentialOptions().forEach(option -> {
+ String id = generateUniqueId();
+ builder.addBeginGetCredentialOption(
+ new BeginGetCredentialOption(
+ id, option.getType(), option.getCandidateQueryData())
+ );
+ beginGetOptionToCredentialOptionMap.put(id, option);
+ });
if (propagateToProvider) {
builder.setCallingAppInfo(callingAppInfo);
}
@@ -152,11 +160,13 @@
int userId, RemoteCredentialService remoteCredentialService,
BeginGetCredentialRequest beginGetRequest,
android.credentials.GetCredentialRequest completeGetRequest,
- CallingAppInfo callingAppInfo) {
+ CallingAppInfo callingAppInfo,
+ Map<String, CredentialOption> beginGetOptionToCredentialOptionMap) {
super(context, info, beginGetRequest, callbacks, userId, remoteCredentialService);
mCompleteRequest = completeGetRequest;
mCallingAppInfo = callingAppInfo;
setStatus(Status.PENDING);
+ mBeginGetOptionToCredentialOptionMap = beginGetOptionToCredentialOptionMap;
}
/** Called when the provider response has been updated by an external source. */
@@ -260,7 +270,7 @@
if (remoteCredentialEntry == null) {
return null;
}
- String entryId = generateEntryId();
+ String entryId = generateUniqueId();
Entry remoteEntry = new Entry(REMOTE_ENTRY_KEY, entryId, remoteCredentialEntry.getSlice());
mUiRemoteEntry = new Pair<>(entryId, remoteCredentialEntry);
return remoteEntry;
@@ -271,7 +281,7 @@
List<Entry> authenticationUiEntries = new ArrayList<>();
for (Action authenticationAction : authenticationEntries) {
- String entryId = generateEntryId();
+ String entryId = generateUniqueId();
mUiAuthenticationEntries.put(entryId, authenticationAction);
authenticationUiEntries.add(new Entry(
AUTHENTICATION_ACTION_ENTRY_KEY, entryId,
@@ -288,7 +298,7 @@
// Populate the credential entries
for (CredentialEntry credentialEntry : credentialEntries) {
- String entryId = generateEntryId();
+ String entryId = generateUniqueId();
mUiCredentialEntries.put(entryId, credentialEntry);
Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
credentialUiEntries.add(new Entry(CREDENTIAL_ENTRY_KEY, entryId,
@@ -298,18 +308,17 @@
return credentialUiEntries;
}
- private Intent setUpFillInIntent(String type) {
- Intent intent = new Intent();
- for (CredentialOption option : mCompleteRequest.getCredentialOptions()) {
- if (option.getType().equals(type)) {
- intent.putExtra(
- CredentialProviderService
- .EXTRA_GET_CREDENTIAL_REQUEST,
- new GetCredentialRequest(mCallingAppInfo, option));
- return intent;
- }
+ private Intent setUpFillInIntent(@Nullable String id) {
+ // TODO: Determine if we should skip this entry if entry id is not set, or is set
+ // but does not resolve to a valid option. For now, not skipping it because
+ // it may be possible that the provider adds their own extras and expects to receive
+ // those and complete the flow.
+ if (id == null || mBeginGetOptionToCredentialOptionMap.get(id) == null) {
+ Log.i(TAG, "Id from Credential Entry does not resolve to a valid option");
}
- return intent;
+ return new Intent().putExtra(CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
+ new GetCredentialRequest(
+ mCallingAppInfo, mBeginGetOptionToCredentialOptionMap.get(id)));
}
private Intent setUpFillInIntentForAuthentication() {
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index 461f447..dd9fcb6 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -109,7 +109,7 @@
// Populate the credential entries
for (CredentialEntry credentialEntry : credentialEntries) {
- String entryId = generateEntryId();
+ String entryId = generateUniqueId();
mUiCredentialEntries.put(entryId, credentialEntry);
Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
credentialUiEntries.add(new Entry(CREDENTIAL_ENTRY_KEY, entryId,
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index d6f97ff..53970f9 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -145,7 +145,7 @@
return Status.CANCELED;
}
- protected String generateEntryId() {
+ protected static String generateUniqueId() {
return UUID.randomUUID().toString();
}
diff --git a/services/tests/BackgroundInstallControlServiceTests/OWNERS b/services/tests/BackgroundInstallControlServiceTests/OWNERS
new file mode 100644
index 0000000..ca84550
--- /dev/null
+++ b/services/tests/BackgroundInstallControlServiceTests/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/transparency/OWNERS
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
new file mode 100644
index 0000000..4fcdbfc
--- /dev/null
+++ b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_test_host {
+ name: "BackgroundInstallControlServiceHostTest",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "tradefed",
+ "compatibility-tradefed",
+ "compatibility-host-util",
+ ],
+ data: [
+ ":BackgroundInstallControlServiceTestApp",
+ ":BackgroundInstallControlMockApp1",
+ ":BackgroundInstallControlMockApp2",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml
new file mode 100644
index 0000000..1e7a78a
--- /dev/null
+++ b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Background Install Control Service integration test">
+ <option name="test-suite-tag" value="apct" />
+
+ <!-- Service is not exposed to apps. Disable SELinux for testing purpose. -->
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="BackgroundInstallControlServiceTestApp.apk" />
+ </target_preparer>
+
+ <!-- Load additional APKs onto device -->
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file"
+ key="BackgroundInstallControlMockApp1.apk"
+ value="/data/local/tmp/BackgroundInstallControlMockApp1.apk" />
+ <option name="push-file"
+ key="BackgroundInstallControlMockApp2.apk"
+ value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" />
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="BackgroundInstallControlServiceHostTest.jar" />
+ </test>
+</configuration>
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java
new file mode 100644
index 0000000..7450607
--- /dev/null
+++ b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.pm.test;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// TODO: Add @Presubmit
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class BackgroundInstallControlServiceHostTest extends BaseHostJUnit4Test {
+ private static final String PACKAGE_NAME = "com.android.server.pm.test.app";
+
+ private static final String MOCK_PACKAGE_NAME_1 = "com.android.servicestests.apps.bicmockapp1";
+ private static final String MOCK_PACKAGE_NAME_2 = "com.android.servicestests.apps.bicmockapp2";
+
+ private static final String TEST_DATA_DIR = "/data/local/tmp/";
+
+ private static final String MOCK_APK_FILE_1 = "BackgroundInstallControlMockApp1.apk";
+ private static final String MOCK_APK_FILE_2 = "BackgroundInstallControlMockApp2.apk";
+
+ @Test
+ public void testGetMockBackgroundInstalledPackages() throws Exception {
+ installPackage(TEST_DATA_DIR + MOCK_APK_FILE_1);
+ installPackage(TEST_DATA_DIR + MOCK_APK_FILE_2);
+
+ assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_1)).isNotNull();
+ assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNotNull();
+
+ assertThat(getDevice().setProperty("debug.transparency.bg-install-apps",
+ MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2)).isTrue();
+ runDeviceTest("testGetMockBackgroundInstalledPackages");
+ assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_1)).isNull();
+ assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_2)).isNull();
+
+ assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_1)).isNull();
+ assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNull();
+ }
+
+ private void installPackage(String path) throws DeviceNotAvailableException {
+ String cmd = "pm install -t --force-queryable " + path;
+ CommandResult result = getDevice().executeShellV2Command(cmd);
+ assertThat(result.getStatus() == CommandStatus.SUCCESS).isTrue();
+ }
+
+ private void runDeviceTest(String method) throws DeviceNotAvailableException {
+ var options = new DeviceTestRunOptions(PACKAGE_NAME);
+ options.setTestClassName(PACKAGE_NAME + ".BackgroundInstallControlServiceTest");
+ options.setTestMethodName(method);
+ runDeviceTests(options);
+ }
+}
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/Android.bp
new file mode 100644
index 0000000..9b39f2d
--- /dev/null
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "BackgroundInstallControlServiceTestApp",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.core",
+ "compatibility-device-util-axt",
+ "junit",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ platform_apis: true,
+ dex_preopt: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..1fa1f84
--- /dev/null
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.app">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="APCT tests for background install control service"
+ android:targetPackage="com.android.server.pm.test.app" />
+</manifest>
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java
new file mode 100644
index 0000000..b74e561
--- /dev/null
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.pm.IBackgroundInstallControlService;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidJUnit4.class)
+public class BackgroundInstallControlServiceTest {
+ private static final String TAG = "BackgroundInstallControlServiceTest";
+
+ private IBackgroundInstallControlService mIBics;
+
+ @Before
+ public void setUp() {
+ mIBics = IBackgroundInstallControlService.Stub.asInterface(
+ ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE));
+ assertThat(mIBics).isNotNull();
+ }
+
+ @Test
+ public void testGetMockBackgroundInstalledPackages() throws RemoteException {
+ ParceledListSlice<PackageInfo> slice = mIBics.getBackgroundInstalledPackages(
+ PackageManager.MATCH_ALL,
+ UserHandle.USER_ALL);
+ assertThat(slice).isNotNull();
+
+ var packageList = slice.getList();
+ assertThat(packageList).isNotNull();
+ assertThat(packageList).hasSize(2);
+
+ var expectedPackageNames = Set.of("com.android.servicestests.apps.bicmockapp1",
+ "com.android.servicestests.apps.bicmockapp2");
+ var actualPackageNames = packageList.stream().map((packageInfo) -> packageInfo.packageName)
+ .collect(Collectors.toSet());
+ assertThat(actualPackageNames).containsExactlyElementsIn(expectedPackageNames);
+ }
+}
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp
new file mode 100644
index 0000000..7804f4c
--- /dev/null
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp
@@ -0,0 +1,52 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_defaults {
+ name: "bic-mock-app-defaults",
+ test_suites: ["device-tests"],
+
+ srcs: ["**/*.java"],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
+
+android_test_helper_app {
+ name: "BackgroundInstallControlMockApp1",
+ defaults: ["bic-mock-app-defaults"],
+ aaptflags: [
+ "--rename-manifest-package com.android.servicestests.apps.bicmockapp1",
+ ],
+}
+
+android_test_helper_app {
+ name: "BackgroundInstallControlMockApp2",
+ defaults: ["bic-mock-app-defaults"],
+ aaptflags: [
+ "--rename-manifest-package com.android.servicestests.apps.bicmockapp2",
+ ],
+}
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/AndroidManifest.xml b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/AndroidManifest.xml
new file mode 100644
index 0000000..3cb39db
--- /dev/null
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.bicmockapp">
+
+ <application android:hasCode="false">
+ </application>
+</manifest>
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/res/values/strings.xml b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/res/values/strings.xml
new file mode 100644
index 0000000..984152f
--- /dev/null
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this placeholder file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="placeholder">placeholder</string>
+</resources>
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 4a40b5f..9c581f9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -711,8 +711,6 @@
null); // description
// Case 8: App1 gets "remove task"
- final String app1Description = "remove task";
-
sleep(1);
final int app1IsolatedUidUser2 = 1099002; // isolated uid
final long app1Pss4 = 34343;
@@ -739,7 +737,7 @@
mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUidUser2, app1UidUser2);
noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_UNKNOWN, app1Description, now8);
+ ApplicationExitInfo.SUBREASON_REMOVE_TASK, null, now8);
updateExitInfo(app, now8);
list.clear();
@@ -749,21 +747,21 @@
info = list.get(0);
verifyApplicationExitInfo(
- info, // info
- now8, // timestamp
- app1PidUser2, // pid
- app1IsolatedUidUser2, // uid
- app1UidUser2, // packageUid
- null, // definingUid
- app1ProcessName, // processName
- 0, // connectionGroup
- ApplicationExitInfo.REASON_OTHER, // reason
- ApplicationExitInfo.SUBREASON_UNKNOWN, // subReason
- 0, // status
- app1Pss4, // pss
- app1Rss4, // rss
- IMPORTANCE_CACHED, // importance
- app1Description); // description
+ info, // info
+ now8, // timestamp
+ app1PidUser2, // pid
+ app1IsolatedUidUser2, // uid
+ app1UidUser2, // packageUid
+ null, // definingUid
+ app1ProcessName, // processName
+ 0, // connectionGroup
+ ApplicationExitInfo.REASON_OTHER, // reason
+ ApplicationExitInfo.SUBREASON_REMOVE_TASK, // subReason
+ 0, // status
+ app1Pss4, // pss
+ app1Rss4, // rss
+ IMPORTANCE_CACHED, // importance
+ null); // description
// App1 gets "too many empty"
final String app1Description2 = "too many empty";
@@ -1058,7 +1056,18 @@
if (importance != null) {
assertEquals(importance.intValue(), info.getImportance());
}
- if (description != null) {
+
+ // info.getDescription returns a combination of subReason & description
+ if ((subReason != null) && (subReason != ApplicationExitInfo.SUBREASON_UNKNOWN)
+ && (description != null)) {
+ assertTrue(TextUtils.equals(
+ "[" + info.subreasonToString(subReason) + "] " + description,
+ info.getDescription()));
+ } else if ((subReason != null) && (subReason != ApplicationExitInfo.SUBREASON_UNKNOWN)) {
+ assertTrue(TextUtils.equals(
+ "[" + info.subreasonToString(subReason) + "]",
+ info.getDescription()));
+ } else if (description != null) {
assertTrue(TextUtils.equals(description, info.getDescription()));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 4d1d2b2..32b9864 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -34,6 +34,7 @@
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
@@ -438,6 +439,42 @@
verify(mMockMagnificationController).setMagnificationFollowTypingEnabled(false);
}
+ @Test
+ public void testSettingsAlwaysOn_setEnabled_featureFlagDisabled_doNothing() {
+ when(mMockMagnificationController.isAlwaysOnMagnificationFeatureFlagEnabled())
+ .thenReturn(false);
+
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ Settings.Secure.putIntForUser(
+ mTestableContext.getContentResolver(),
+ // TODO: replace name with Settings Secure Key
+ "accessibility_magnification_always_on_enabled",
+ 1, mA11yms.getCurrentUserIdLocked());
+
+ mA11yms.readAlwaysOnMagnificationLocked(userState);
+
+ verify(mMockMagnificationController, never()).setAlwaysOnMagnificationEnabled(anyBoolean());
+ }
+
+ @Test
+ public void testSettingsAlwaysOn_setEnabled_featureFlagEnabled_propagateToController() {
+ when(mMockMagnificationController.isAlwaysOnMagnificationFeatureFlagEnabled())
+ .thenReturn(true);
+
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ Settings.Secure.putIntForUser(
+ mTestableContext.getContentResolver(),
+ // TODO: replace name with Settings Secure Key
+ "accessibility_magnification_always_on_enabled",
+ 1, mA11yms.getCurrentUserIdLocked());
+
+ mA11yms.readAlwaysOnMagnificationLocked(userState);
+
+ verify(mMockMagnificationController).setAlwaysOnMagnificationEnabled(eq(true));
+ }
+
@SmallTest
@Test
public void testOnClientChange_magnificationEnabledAndCapabilityAll_requestConnection() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index ed0336a..b4558b2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -179,6 +179,7 @@
assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked());
assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked());
assertTrue(mUserState.isMagnificationFollowTypingEnabled());
+ assertFalse(mUserState.isAlwaysOnMagnificationEnabled());
}
@Test
@@ -390,6 +391,15 @@
}
@Test
+ public void setAlwaysOnMagnificationEnabled_defaultFalseAndSetTrue_returnTrue() {
+ assertFalse(mUserState.isAlwaysOnMagnificationEnabled());
+
+ mUserState.setAlwaysOnMagnificationEnabled(true);
+
+ assertTrue(mUserState.isAlwaysOnMagnificationEnabled());
+ }
+
+ @Test
public void setFocusAppearanceData_returnExpectedFocusAppearanceData() {
final int focusStrokeWidthValue = 100;
final int focusColorValue = Color.BLUE;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index bf23d9d..2d036fe 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -1196,6 +1196,27 @@
persistedScale);
}
+ @Test
+ public void testOnContextChanged_alwaysOnFeatureDisabled_resetMagnification() {
+ setScaleToMagnifying();
+
+ mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(false);
+ mFullScreenMagnificationController.onUserContextChanged(DISPLAY_0);
+
+ verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(DISPLAY_0), eq(false));
+ }
+
+ @Test
+ public void testOnContextChanged_alwaysOnFeatureEnabled_setScaleTo1xAndStayActivated() {
+ setScaleToMagnifying();
+
+ mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true);
+ mFullScreenMagnificationController.onUserContextChanged(DISPLAY_0);
+
+ assertEquals(1.0f, mFullScreenMagnificationController.getScale(DISPLAY_0), 0);
+ assertTrue(mFullScreenMagnificationController.isActivated(DISPLAY_0));
+ }
+
private void setScaleToMagnifying() {
register(DISPLAY_0);
float scale = 2.0f;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 5334e4c..a095760 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -81,23 +81,27 @@
* IDLE -> SHORTCUT_TRIGGERED [label="a11y\nbtn"]
* IDLE -> DOUBLE_TAP [label="2tap"]
* DOUBLE_TAP -> IDLE [label="timeout"]
- * DOUBLE_TAP -> ZOOMED [label="tap"]
+ * DOUBLE_TAP -> ACTIVATED [label="tap"]
* DOUBLE_TAP -> NON_ACTIVATED_ZOOMED_TMP [label="hold"]
* NON_ACTIVATED_ZOOMED_TMP -> IDLE [label="release"]
* SHORTCUT_TRIGGERED -> IDLE [label="a11y\nbtn"]
- * SHORTCUT_TRIGGERED -> ZOOMED[label="tap"]
- * SHORTCUT_TRIGGERED -> ACTIVATED_ZOOMED_TMP [label="hold"]
+ * SHORTCUT_TRIGGERED -> ACTIVATED [label="tap"]
+ * SHORTCUT_TRIGGERED -> SHORTCUT_TRIGGERED_ZOOMED_TMP [label="hold"]
* SHORTCUT_TRIGGERED -> PANNING [label="2hold]
- * ZOOMED -> ZOOMED_DOUBLE_TAP [label="2tap"]
- * ZOOMED -> IDLE [label="a11y\nbtn"]
- * ZOOMED -> PANNING [label="2hold"]
- * ZOOMED_DOUBLE_TAP -> ZOOMED [label="timeout"]
- * ZOOMED_DOUBLE_TAP -> ACTIVATED_ZOOMED_TMP [label="hold"]
- * ZOOMED_DOUBLE_TAP -> IDLE [label="tap"]
- * ACTIVATED_ZOOMED_TMP -> ZOOMED [label="release"]
- * PANNING -> ZOOMED [label="release"]
+ * if always-on enabled:
+ * SHORTCUT_TRIGGERED_ZOOMED_TMP -> ACTIVATED [label="release"]
+ * else:
+ * SHORTCUT_TRIGGERED_ZOOMED_TMP -> IDLE [label="release"]
+ * ACTIVATED -> ACTIVATED_DOUBLE_TAP [label="2tap"]
+ * ACTIVATED -> IDLE [label="a11y\nbtn"]
+ * ACTIVATED -> PANNING [label="2hold"]
+ * ACTIVATED_DOUBLE_TAP -> ACTIVATED [label="timeout"]
+ * ACTIVATED_DOUBLE_TAP -> ACTIVATED_ZOOMED_TMP [label="hold"]
+ * ACTIVATED_DOUBLE_TAP -> IDLE [label="tap"]
+ * ACTIVATED_ZOOMED_TMP -> ACTIVATED [label="release"]
+ * PANNING -> ACTIVATED [label="release"]
* PANNING -> PANNING_SCALING [label="pinch"]
- * PANNING_SCALING -> ZOOMED [label="release"]
+ * PANNING_SCALING -> ACTIVATED [label="release"]
* }
* }
*/
@@ -105,14 +109,15 @@
public class FullScreenMagnificationGestureHandlerTest {
public static final int STATE_IDLE = 1;
- public static final int STATE_ZOOMED = 2;
+ public static final int STATE_ACTIVATED = 2;
public static final int STATE_2TAPS = 3;
- public static final int STATE_ZOOMED_2TAPS = 4;
+ public static final int STATE_ACTIVATED_2TAPS = 4;
public static final int STATE_SHORTCUT_TRIGGERED = 5;
public static final int STATE_NON_ACTIVATED_ZOOMED_TMP = 6;
public static final int STATE_ACTIVATED_ZOOMED_TMP = 7;
- public static final int STATE_PANNING = 8;
- public static final int STATE_SCALING_AND_PANNING = 9;
+ public static final int STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP = 8;
+ public static final int STATE_PANNING = 9;
+ public static final int STATE_SCALING_AND_PANNING = 10;
public static final int FIRST_STATE = STATE_IDLE;
public static final int LAST_STATE = STATE_SCALING_AND_PANNING;
@@ -167,6 +172,7 @@
}
};
mFullScreenMagnificationController.register(DISPLAY_0);
+ mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true);
mClock = new OffsettableClock.Stopped();
boolean detectTripleTap = true;
@@ -267,22 +273,22 @@
assertTransition(STATE_SHORTCUT_TRIGGERED, () -> {
send(downEvent());
fastForward1sec();
- }, STATE_ACTIVATED_ZOOMED_TMP);
+ }, STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP);
- // A11y button followed by a tap turns zoom on
- assertTransition(STATE_SHORTCUT_TRIGGERED, () -> tap(), STATE_ZOOMED);
+ // A11y button followed by a tap turns magnifier on
+ assertTransition(STATE_SHORTCUT_TRIGGERED, () -> tap(), STATE_ACTIVATED);
// A11y button pressed second time negates the 1st press
assertTransition(STATE_SHORTCUT_TRIGGERED, () -> triggerShortcut(), STATE_IDLE);
- // A11y button turns zoom off
- assertTransition(STATE_ZOOMED, () -> triggerShortcut(), STATE_IDLE);
+ // A11y button turns magnifier off
+ assertTransition(STATE_ACTIVATED, () -> triggerShortcut(), STATE_IDLE);
- // Double tap times out while zoomed
- assertTransition(STATE_ZOOMED_2TAPS, () -> {
+ // Double tap times out while activated
+ assertTransition(STATE_ACTIVATED_2TAPS, () -> {
allowEventDelegation();
fastForward1sec();
- }, STATE_ZOOMED);
+ }, STATE_ACTIVATED);
// tap+tap+swipe doesn't get delegated
assertTransition(STATE_2TAPS, () -> swipe(), STATE_IDLE);
@@ -290,8 +296,29 @@
// tap+tap+swipe&hold initiates temporary viewport dragging zoom in immediately
assertTransition(STATE_2TAPS, () -> swipeAndHold(), STATE_NON_ACTIVATED_ZOOMED_TMP);
- // release when activated temporary zoom in back to zoomed
- assertTransition(STATE_ACTIVATED_ZOOMED_TMP, () -> upEvent(), STATE_ZOOMED);
+ // release when activated temporary zoom in back to activated
+ assertTransition(STATE_ACTIVATED_ZOOMED_TMP, () -> send(upEvent()), STATE_ACTIVATED);
+ }
+
+ @Test
+ public void testRelease_shortcutTriggeredZoomedTmp_alwaysOnNotEnabled_shouldInIdle() {
+ mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(false);
+ goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP);
+ send(upEvent());
+
+ assertIn(STATE_IDLE);
+ }
+
+ @Test
+ public void testRelease_shortcutTriggeredZoomedTmp_alwaysOnEnabled_shouldInActivated() {
+ mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true);
+ goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP);
+ send(upEvent());
+
+ assertIn(STATE_ACTIVATED);
+ assertTrue(!isZoomed());
+
+ returnToNormalFrom(STATE_ACTIVATED);
}
@Test
@@ -310,7 +337,7 @@
longTap();
};
assertStaysIn(STATE_IDLE, tapAndLongTap);
- assertStaysIn(STATE_ZOOMED, tapAndLongTap);
+ assertStaysIn(STATE_ACTIVATED, tapAndLongTap);
// Triple tap with delays in between doesn't count
Runnable slow3tap = () -> {
@@ -321,7 +348,7 @@
tap();
};
assertStaysIn(STATE_IDLE, slow3tap);
- assertStaysIn(STATE_ZOOMED, slow3tap);
+ assertStaysIn(STATE_ACTIVATED, slow3tap);
}
@Test
@@ -337,9 +364,9 @@
@Test
public void testTripleTapAndHold_zoomsImmediately() {
assertZoomsImmediatelyOnSwipeFrom(STATE_2TAPS, STATE_NON_ACTIVATED_ZOOMED_TMP);
- assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED, STATE_ACTIVATED_ZOOMED_TMP);
- assertZoomsImmediatelyOnSwipeFrom(STATE_ZOOMED_2TAPS, STATE_ACTIVATED_ZOOMED_TMP);
-
+ assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED,
+ STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP);
+ assertZoomsImmediatelyOnSwipeFrom(STATE_ACTIVATED_2TAPS, STATE_ACTIVATED_ZOOMED_TMP);
}
@Test
@@ -361,8 +388,8 @@
}
@Test
- public void testTwoFingersOneTap_zoomedState_dispatchMotionEvents() {
- goFromStateIdleTo(STATE_ZOOMED);
+ public void testTwoFingersOneTap_activatedState_dispatchMotionEvents() {
+ goFromStateIdleTo(STATE_ACTIVATED);
final EventCaptor eventCaptor = new EventCaptor();
mMgh.setNext(eventCaptor);
@@ -371,7 +398,7 @@
send(pointerEvent(ACTION_POINTER_UP, DEFAULT_X * 2, DEFAULT_Y));
send(upEvent());
- assertIn(STATE_ZOOMED);
+ assertIn(STATE_ACTIVATED);
final List<Integer> expectedActions = new ArrayList();
expectedActions.add(Integer.valueOf(ACTION_DOWN));
expectedActions.add(Integer.valueOf(ACTION_POINTER_DOWN));
@@ -379,12 +406,12 @@
expectedActions.add(Integer.valueOf(ACTION_UP));
assertActionsInOrder(eventCaptor.mEvents, expectedActions);
- returnToNormalFrom(STATE_ZOOMED);
+ returnToNormalFrom(STATE_ACTIVATED);
}
@Test
- public void testThreeFingersOneTap_zoomedState_dispatchMotionEvents() {
- goFromStateIdleTo(STATE_ZOOMED);
+ public void testThreeFingersOneTap_activatedState_dispatchMotionEvents() {
+ goFromStateIdleTo(STATE_ACTIVATED);
final EventCaptor eventCaptor = new EventCaptor();
mMgh.setNext(eventCaptor);
PointF pointer1 = DEFAULT_POINT;
@@ -398,7 +425,7 @@
send(pointerEvent(ACTION_POINTER_UP, new PointF[] {pointer1, pointer2, pointer3}, 2));
send(upEvent());
- assertIn(STATE_ZOOMED);
+ assertIn(STATE_ACTIVATED);
final List<Integer> expectedActions = new ArrayList();
expectedActions.add(Integer.valueOf(ACTION_DOWN));
expectedActions.add(Integer.valueOf(ACTION_POINTER_DOWN));
@@ -408,12 +435,12 @@
expectedActions.add(Integer.valueOf(ACTION_UP));
assertActionsInOrder(eventCaptor.mEvents, expectedActions);
- returnToNormalFrom(STATE_ZOOMED);
+ returnToNormalFrom(STATE_ACTIVATED);
}
@Test
- public void testFirstFingerSwipe_twoPointerDownAndZoomedState_panningState() {
- goFromStateIdleTo(STATE_ZOOMED);
+ public void testFirstFingerSwipe_twoPointerDownAndActivatedState_panningState() {
+ goFromStateIdleTo(STATE_ACTIVATED);
PointF pointer1 = DEFAULT_POINT;
PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
@@ -429,8 +456,8 @@
}
@Test
- public void testSecondFingerSwipe_twoPointerDownAndZoomedState_panningState() {
- goFromStateIdleTo(STATE_ZOOMED);
+ public void testSecondFingerSwipe_twoPointerDownAndActivatedState_panningState() {
+ goFromStateIdleTo(STATE_ACTIVATED);
PointF pointer1 = DEFAULT_POINT;
PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
@@ -463,8 +490,8 @@
}
@Test
- public void testZoomedWithTripleTap_invokeShowWindowPromptAction() {
- goFromStateIdleTo(STATE_ZOOMED);
+ public void testActivatedWithTripleTap_invokeShowWindowPromptAction() {
+ goFromStateIdleTo(STATE_ACTIVATED);
verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
}
@@ -541,9 +568,8 @@
check(!isActivated(), state);
check(!isZoomed(), state);
} break;
- case STATE_ZOOMED: {
+ case STATE_ACTIVATED: {
check(isActivated(), state);
- check(isZoomed(), state);
check(tapCount() < 2, state);
} break;
case STATE_2TAPS: {
@@ -551,7 +577,7 @@
check(!isZoomed(), state);
check(tapCount() == 2, state);
} break;
- case STATE_ZOOMED_2TAPS: {
+ case STATE_ACTIVATED_2TAPS: {
check(isActivated(), state);
check(isZoomed(), state);
check(tapCount() == 2, state);
@@ -561,14 +587,29 @@
check(isZoomed(), state);
check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
state);
- check(!mMgh.mViewportDraggingState.mActivatedBeforeDrag, state);
+ check(Float.isNaN(mMgh.mViewportDraggingState.mScaleToRecoverAfterDraggingEnd),
+ state);
} break;
case STATE_ACTIVATED_ZOOMED_TMP: {
check(isActivated(), state);
check(isZoomed(), state);
check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
state);
- check(mMgh.mViewportDraggingState.mActivatedBeforeDrag, state);
+ check(mMgh.mViewportDraggingState.mScaleToRecoverAfterDraggingEnd >= 1.0f,
+ state);
+ } break;
+ case STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP: {
+ check(isActivated(), state);
+ check(isZoomed(), state);
+ check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
+ state);
+ if (mFullScreenMagnificationController.isAlwaysOnMagnificationEnabled()) {
+ check(mMgh.mViewportDraggingState.mScaleToRecoverAfterDraggingEnd >= 1.0f,
+ state);
+ } else {
+ check(Float.isNaN(mMgh.mViewportDraggingState.mScaleToRecoverAfterDraggingEnd),
+ state);
+ }
} break;
case STATE_SHORTCUT_TRIGGERED: {
check(mMgh.mDetectingState.mShortcutTriggered, state);
@@ -605,7 +646,7 @@
tap();
tap();
} break;
- case STATE_ZOOMED: {
+ case STATE_ACTIVATED: {
if (mMgh.mDetectTripleTap) {
goFromStateIdleTo(STATE_2TAPS);
tap();
@@ -614,8 +655,8 @@
tap();
}
} break;
- case STATE_ZOOMED_2TAPS: {
- goFromStateIdleTo(STATE_ZOOMED);
+ case STATE_ACTIVATED_2TAPS: {
+ goFromStateIdleTo(STATE_ACTIVATED);
tap();
tap();
} break;
@@ -625,7 +666,12 @@
fastForward1sec();
} break;
case STATE_ACTIVATED_ZOOMED_TMP: {
- goFromStateIdleTo(STATE_ZOOMED_2TAPS);
+ goFromStateIdleTo(STATE_ACTIVATED_2TAPS);
+ send(downEvent());
+ fastForward1sec();
+ } break;
+ case STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP: {
+ goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
send(downEvent());
fastForward1sec();
} break;
@@ -634,7 +680,7 @@
triggerShortcut();
} break;
case STATE_PANNING: {
- goFromStateIdleTo(STATE_ZOOMED);
+ goFromStateIdleTo(STATE_ACTIVATED);
send(downEvent());
send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
fastForward(ViewConfiguration.getTapTimeout());
@@ -665,16 +711,16 @@
allowEventDelegation();
fastForward1sec();
} break;
- case STATE_ZOOMED: {
+ case STATE_ACTIVATED: {
if (mMgh.mDetectTripleTap) {
tap();
tap();
- returnToNormalFrom(STATE_ZOOMED_2TAPS);
+ returnToNormalFrom(STATE_ACTIVATED_2TAPS);
} else {
triggerShortcut();
}
} break;
- case STATE_ZOOMED_2TAPS: {
+ case STATE_ACTIVATED_2TAPS: {
tap();
} break;
case STATE_NON_ACTIVATED_ZOOMED_TMP: {
@@ -682,7 +728,13 @@
} break;
case STATE_ACTIVATED_ZOOMED_TMP: {
send(upEvent());
- returnToNormalFrom(STATE_ZOOMED);
+ returnToNormalFrom(STATE_ACTIVATED);
+ } break;
+ case STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP: {
+ send(upEvent());
+ if (mFullScreenMagnificationController.isAlwaysOnMagnificationEnabled()) {
+ returnToNormalFrom(STATE_ACTIVATED);
+ }
} break;
case STATE_SHORTCUT_TRIGGERED: {
triggerShortcut();
@@ -690,7 +742,7 @@
case STATE_PANNING: {
send(pointerEvent(ACTION_POINTER_UP, DEFAULT_X * 2, DEFAULT_Y));
send(upEvent());
- returnToNormalFrom(STATE_ZOOMED);
+ returnToNormalFrom(STATE_ACTIVATED);
} break;
case STATE_SCALING_AND_PANNING: {
returnToNormalFrom(STATE_PANNING);
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 407c575..b4a16c2 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
@@ -739,6 +739,13 @@
}
@Test
+ public void setPreferenceAlwaysOnMagnificationEnabled_setPrefEnabled_enableOnFullScreen() {
+ mMagnificationController.setAlwaysOnMagnificationEnabled(true);
+
+ verify(mScreenMagnificationController).setAlwaysOnMagnificationEnabled(eq(true));
+ }
+
+ @Test
public void onRectangleOnScreenRequested_fullScreenIsActivated_fullScreenDispatchEvent() {
mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY,
true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
index 0568b38..c0a90b2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
@@ -182,13 +182,11 @@
@Test
public void testApplicationNotInFocusWithModeId() {
final WindowState appWindow = createWindow("appWindow");
- assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);
-
- final WindowState inFocusWindow = createWindow("inFocus");
- appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
-
+ mDisplayContent.mCurrentFocus = appWindow;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ mDisplayContent.mCurrentFocus = null;
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+
// The window is not in focus.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);
@@ -211,13 +209,11 @@
@Test
public void testApplicationNotInFocusWithoutModeId() {
final WindowState appWindow = createWindow("appWindow");
- assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);
-
- final WindowState inFocusWindow = createWindow("inFocus");
- appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
-
+ mDisplayContent.mCurrentFocus = appWindow;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ mDisplayContent.mCurrentFocus = null;
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+
// The window is not in focus.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 40e8273..060f9d8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -352,6 +352,8 @@
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ // Flush EVENT_APPEARED.
+ mController.dispatchPendingEvents();
final Task task = activity.getTask();
activity.info.applicationInfo.uid = uid;
doReturn(pid).when(activity).getPid();
@@ -379,6 +381,8 @@
DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
activity.reparent(taskFragment, POSITION_TOP);
activity.mLastTaskFragmentOrganizerBeforePip = null;
+ // Flush EVENT_INFO_CHANGED.
+ mController.dispatchPendingEvents();
// Clear invocations now because there will be another transaction for the TaskFragment
// change above, triggered by the reparent. We only want to test onActivityReparentedToTask
@@ -400,6 +404,8 @@
final Task task = createTask(mDisplayContent);
task.addChild(mTaskFragment, POSITION_TOP);
final ActivityRecord activity = createActivityRecord(task);
+ // Flush EVENT_APPEARED.
+ mController.dispatchPendingEvents();
// Make sure the activity belongs to the same app, but it is in a different pid.
activity.info.applicationInfo.uid = uid;
@@ -442,6 +448,8 @@
final Task task = createTask(mDisplayContent);
task.addChild(mTaskFragment, POSITION_TOP);
final ActivityRecord activity = createActivityRecord(task);
+ // Flush EVENT_APPEARED.
+ mController.dispatchPendingEvents();
// Make sure the activity is embedded in untrusted mode.
activity.info.applicationInfo.uid = uid + 1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index b2761d1..169586e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -871,6 +871,7 @@
lastReportedTiles.clear();
called[0] = false;
task1.positionChildAt(POSITION_TOP, rootTask, false /* includingParents */);
+ mAtm.mTaskOrganizerController.dispatchPendingEvents();
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
@@ -1124,11 +1125,11 @@
final Task rootTask = createRootTask();
final Task task = createTask(rootTask);
- final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
+ final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
- waitUntilHandlersIdle();
+ mAtm.mTaskOrganizerController.dispatchPendingEvents();
assertEquals("TestDescription", o.mChangedInfo.taskDescription.getLabel());
}
@@ -1288,6 +1289,8 @@
public void testAppearDeferThenInfoChange() {
final ITaskOrganizer organizer = registerMockOrganizer();
final Task rootTask = createRootTask();
+ // Flush EVENT_APPEARED.
+ mAtm.mTaskOrganizerController.dispatchPendingEvents();
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
@@ -1310,6 +1313,8 @@
public void testAppearDeferThenVanish() {
final ITaskOrganizer organizer = registerMockOrganizer();
final Task rootTask = createRootTask();
+ // Flush EVENT_APPEARED.
+ mAtm.mTaskOrganizerController.dispatchPendingEvents();
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
@@ -1328,7 +1333,7 @@
final ITaskOrganizer organizer = registerMockOrganizer();
final Task rootTask = createRootTask();
final Task task = createTask(rootTask);
- final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
+ final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
@@ -1358,7 +1363,7 @@
final ITaskOrganizer organizer = registerMockOrganizer();
final Task rootTask = createRootTask();
final Task task = createTask(rootTask);
- final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
+ final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
@@ -1381,7 +1386,7 @@
final ITaskOrganizer organizer = registerMockOrganizer();
final Task rootTask = createRootTask();
final Task task = createTask(rootTask);
- final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
+ createActivityRecordAndDispatchPendingEvents(task);
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
@@ -1400,7 +1405,7 @@
final ITaskOrganizer organizer = registerMockOrganizer();
final Task rootTask = createRootTask();
final Task task = createTask(rootTask);
- final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
+ final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
@@ -1465,13 +1470,12 @@
final ITaskOrganizer organizer = registerMockOrganizer();
final Task rootTask = createRootTask();
final Task task = createTask(rootTask);
- final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
+ final ActivityRecord activity = createActivityRecordAndDispatchPendingEvents(task);
final ArgumentCaptor<RunningTaskInfo> infoCaptor =
ArgumentCaptor.forClass(RunningTaskInfo.class);
assertTrue(rootTask.isOrganized());
- spyOn(activity);
doReturn(true).when(activity).inSizeCompatMode();
doReturn(true).when(activity).isState(RESUMED);
@@ -1550,6 +1554,13 @@
verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
+ private ActivityRecord createActivityRecordAndDispatchPendingEvents(Task task) {
+ final ActivityRecord record = createActivityRecord(task);
+ // Flush EVENT_APPEARED.
+ mAtm.mTaskOrganizerController.dispatchPendingEvents();
+ return record;
+ }
+
/**
* Verifies that task vanished is called for a specific task.
*/
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 17b44ee..d13fb1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -48,7 +48,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -103,7 +102,6 @@
import androidx.test.filters.SmallTest;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -124,15 +122,6 @@
@RunWith(WindowTestRunner.class)
public class WindowStateTests extends WindowTestsBase {
- @Before
- public void setUp() {
- // TODO: Let the insets source with new mode keep the visibility control, and remove this
- // setup code. Now mTopFullscreenOpaqueWindowState will take back the control of insets
- // visibility.
- spyOn(mDisplayContent);
- doNothing().when(mDisplayContent).layoutAndAssignWindowLayersIfNeeded();
- }
-
@Test
public void testIsParentWindowHidden() {
final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index acfc194..5e15412 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8989,8 +8989,9 @@
"carrier_certificate_string_array";
/**
- * Flag specifying whether the incoming call number should be formatted to national number
- * for Japan. @return {@code true} convert to the national format, {@code false} otherwise.
+ * Flag specifying whether the incoming call number and the conference participant number
+ * should be formatted to national number for Japan.
+ * @return {@code true} convert to the national format, {@code false} otherwise.
* e.g. "+819012345678" -> "09012345678"
* @hide
*/
@@ -10059,7 +10060,7 @@
sDefaults.putAll(Bsf.getDefaults());
sDefaults.putAll(Iwlan.getDefaults());
sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[0]);
- sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false);
+ sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false);
sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
new int[] {4 /* BUSY */});
sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
diff --git a/telephony/java/android/telephony/ims/MediaQualityStatus.java b/telephony/java/android/telephony/ims/MediaQualityStatus.java
index 5038aac..76394fe 100644
--- a/telephony/java/android/telephony/ims/MediaQualityStatus.java
+++ b/telephony/java/android/telephony/ims/MediaQualityStatus.java
@@ -40,7 +40,7 @@
private final int mMediaSessionType;
private final int mTransportType;
private final int mRtpPacketLossRate;
- private final int mRtpJitter;
+ private final int mRtpJitterMillis;
private final long mRtpInactivityTimeMillis;
/** @hide */
@@ -52,23 +52,26 @@
public @interface MediaSessionType {}
/**
- * Constructor for this
+ * The constructor for MediaQualityStatus, which represents the media quality for each session
+ * type ({@link #MEDIA_SESSION_TYPE_AUDIO} or {@link #MEDIA_SESSION_TYPE_VIDEO}) of the IMS call
*
* @param imsCallSessionId IMS call session id of this quality status
* @param mediaSessionType media session type of this quality status
* @param transportType transport type of this quality status
- * @param rtpPacketLossRate measured RTP packet loss rate
- * @param rtpJitter measured RTP jitter value
+ * @param rtpPacketLossRate measured RTP packet loss rate in percentage
+ * @param rtpJitterMillis measured RTP jitter(RFC3550) in milliseconds
* @param rptInactivityTimeMillis measured RTP inactivity time in milliseconds
*/
- private MediaQualityStatus(@NonNull String imsCallSessionId,
+ public MediaQualityStatus(@NonNull String imsCallSessionId,
@MediaSessionType int mediaSessionType, @TransportType int transportType,
- int rtpPacketLossRate, int rtpJitter, long rptInactivityTimeMillis) {
+ @IntRange(from = 0, to = 100) int rtpPacketLossRate,
+ @IntRange(from = 0) int rtpJitterMillis,
+ @IntRange(from = 0) long rptInactivityTimeMillis) {
mImsCallSessionId = imsCallSessionId;
mMediaSessionType = mediaSessionType;
mTransportType = transportType;
mRtpPacketLossRate = rtpPacketLossRate;
- mRtpJitter = rtpJitter;
+ mRtpJitterMillis = rtpJitterMillis;
mRtpInactivityTimeMillis = rptInactivityTimeMillis;
}
@@ -106,13 +109,15 @@
/**
* Retrieves measured RTP jitter(RFC3550) value in milliseconds
*/
+ @IntRange(from = 0)
public int getRtpJitterMillis() {
- return mRtpJitter;
+ return mRtpJitterMillis;
}
/**
* Retrieves measured RTP inactivity time in milliseconds
*/
+ @IntRange(from = 0)
public long getRtpInactivityMillis() {
return mRtpInactivityTimeMillis;
}
@@ -126,7 +131,7 @@
mMediaSessionType = in.readInt();
mTransportType = in.readInt();
mRtpPacketLossRate = in.readInt();
- mRtpJitter = in.readInt();
+ mRtpJitterMillis = in.readInt();
mRtpInactivityTimeMillis = in.readLong();
}
@@ -136,7 +141,7 @@
dest.writeInt(mMediaSessionType);
dest.writeInt(mTransportType);
dest.writeInt(mRtpPacketLossRate);
- dest.writeInt(mRtpJitter);
+ dest.writeInt(mRtpJitterMillis);
dest.writeLong(mRtpInactivityTimeMillis);
}
@@ -167,14 +172,14 @@
&& mMediaSessionType == that.mMediaSessionType
&& mTransportType == that.mTransportType
&& mRtpPacketLossRate == that.mRtpPacketLossRate
- && mRtpJitter == that.mRtpJitter
+ && mRtpJitterMillis == that.mRtpJitterMillis
&& mRtpInactivityTimeMillis == that.mRtpInactivityTimeMillis;
}
@Override
public int hashCode() {
return Objects.hash(mImsCallSessionId, mMediaSessionType, mTransportType,
- mRtpPacketLossRate, mRtpJitter, mRtpInactivityTimeMillis);
+ mRtpPacketLossRate, mRtpJitterMillis, mRtpInactivityTimeMillis);
}
@Override
@@ -188,100 +193,11 @@
sb.append(mTransportType);
sb.append(", mRtpPacketLossRate=");
sb.append(mRtpPacketLossRate);
- sb.append(", mRtpJitter=");
- sb.append(mRtpJitter);
+ sb.append(", mRtpJitterMillis=");
+ sb.append(mRtpJitterMillis);
sb.append(", mRtpInactivityTimeMillis=");
sb.append(mRtpInactivityTimeMillis);
sb.append("}");
return sb.toString();
}
-
- /**
- * Provides a convenient way to set the fields of an {@link MediaQualityStatus} when creating a
- * new instance.
- *
- * <p>The example below shows how you might create a new {@code RtpQualityStatus}:
- *
- * <pre><code>
- *
- * MediaQualityStatus = new MediaQualityStatus.Builder(
- * callSessionId, mediaSessionType, transportType)
- * .setRtpPacketLossRate(packetLossRate)
- * .setRtpJitter(jitter)
- * .setRtpInactivityMillis(inactivityTimeMillis)
- * .build();
- * </code></pre>
- */
- public static final class Builder {
- private final String mImsCallSessionId;
- private final int mMediaSessionType;
- private final int mTransportType;
- private int mRtpPacketLossRate;
- private int mRtpJitter;
- private long mRtpInactivityTimeMillis;
-
- /**
- * Default constructor for the Builder.
- */
- public Builder(
- @NonNull String imsCallSessionId,
- @MediaSessionType int mediaSessionType,
- @TransportType int transportType) {
- mImsCallSessionId = imsCallSessionId;
- mMediaSessionType = mediaSessionType;
- mTransportType = transportType;
- }
-
- /**
- * Set RTP packet loss info.
- *
- * @param packetLossRate RTP packet loss rate in percentage
- * @return The same instance of the builder.
- */
- @NonNull
- public Builder setRtpPacketLossRate(@IntRange(from = 0, to = 100) int packetLossRate) {
- this.mRtpPacketLossRate = packetLossRate;
- return this;
- }
-
- /**
- * Set calculated RTP jitter(RFC3550) value in milliseconds.
- *
- * @param jitter calculated RTP jitter value.
- * @return The same instance of the builder.
- */
- @NonNull
- public Builder setRtpJitterMillis(int jitter) {
- this.mRtpJitter = jitter;
- return this;
- }
-
- /**
- * Set measured RTP inactivity time.
- *
- * @param inactivityTimeMillis RTP inactivity time in Milliseconds.
- * @return The same instance of the builder.
- */
- @NonNull
- public Builder setRtpInactivityMillis(long inactivityTimeMillis) {
- this.mRtpInactivityTimeMillis = inactivityTimeMillis;
- return this;
- }
-
- /**
- * Build the {@link MediaQualityStatus}
- *
- * @return the {@link MediaQualityStatus} object
- */
- @NonNull
- public MediaQualityStatus build() {
- return new MediaQualityStatus(
- mImsCallSessionId,
- mMediaSessionType,
- mTransportType,
- mRtpPacketLossRate,
- mRtpJitter,
- mRtpInactivityTimeMillis);
- }
- }
}
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index 6d4ffcf..3567c08 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -92,7 +92,7 @@
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction(INTENT_ACTION);
- registerReceiver(mBroadcastReceiver, filter);
+ registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
// Make sure the data directory exists, and we're the owner of it.
try {
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index b43e4f7..9d9d24b 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -19,7 +19,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.ComponentName;
@@ -114,8 +113,9 @@
*
* Automatically binds to implementation of {@link SharedConnectivityService} specified in
* device overlay.
+ *
+ * @hide
*/
- @SuppressLint("ManagerConstructor")
public SharedConnectivityManager(@NonNull Context context) {
ServiceConnection serviceConnection = new ServiceConnection() {
@Override