Merge "Baseline NewApi issues" into udc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index 5b9970b..6078712f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -26782,9 +26782,7 @@
}
public final class TableResponse extends android.media.tv.BroadcastInfoResponse implements android.os.Parcelable {
- ctor public TableResponse(int, int, int, @Nullable android.net.Uri, int, int);
- ctor public TableResponse(int, int, int, @NonNull byte[], int, int);
- ctor public TableResponse(int, int, int, @NonNull android.os.SharedMemory, int, int);
+ ctor @Deprecated public TableResponse(int, int, int, @Nullable android.net.Uri, int, int);
method public int getSize();
method @Nullable public byte[] getTableByteArray();
method @Nullable public android.os.SharedMemory getTableSharedMemory();
@@ -26793,6 +26791,14 @@
field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TableResponse> CREATOR;
}
+ public static final class TableResponse.Builder {
+ ctor public TableResponse.Builder(int, int, int, int, int);
+ method @NonNull public android.media.tv.TableResponse build();
+ method @NonNull public android.media.tv.TableResponse.Builder setTableByteArray(@NonNull byte[]);
+ method @NonNull public android.media.tv.TableResponse.Builder setTableSharedMemory(@NonNull android.os.SharedMemory);
+ method @NonNull public android.media.tv.TableResponse.Builder setTableUri(@NonNull android.net.Uri);
+ }
+
public final class TimelineRequest extends android.media.tv.BroadcastInfoRequest implements android.os.Parcelable {
ctor public TimelineRequest(int, int, int);
ctor public TimelineRequest(int, int, int, @NonNull String);
@@ -27337,8 +27343,9 @@
field public static final int TIME_SHIFT_STATUS_UNKNOWN = 0; // 0x0
field public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1; // 0x1
field public static final String TV_MESSAGE_KEY_STREAM_ID = "android.media.tv.TvInputManager.stream_id";
- field public static final String TV_MESSAGE_TYPE_CLOSED_CAPTION = "CC";
- field public static final String TV_MESSAGE_TYPE_WATERMARK = "Watermark";
+ field public static final int TV_MESSAGE_TYPE_CLOSED_CAPTION = 2; // 0x2
+ field public static final int TV_MESSAGE_TYPE_OTHER = 1000; // 0x3e8
+ field public static final int TV_MESSAGE_TYPE_WATERMARK = 1; // 0x1
field public static final int VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 4; // 0x4
field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3
field public static final int VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT = 16; // 0x10
@@ -27429,10 +27436,10 @@
method public void notifyTrackSelected(int, String);
method public void notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>);
method public void notifyTuned(@NonNull android.net.Uri);
- method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
+ method public void notifyTvMessage(int, @NonNull android.os.Bundle);
method public void notifyVideoAvailable();
method public void notifyVideoUnavailable(int);
- method public void onAdBuffer(@NonNull android.media.tv.AdBuffer);
+ method public void onAdBufferReady(@NonNull android.media.tv.AdBuffer);
method public void onAppPrivateCommand(@NonNull String, android.os.Bundle);
method public android.view.View onCreateOverlayView();
method public boolean onGenericMotionEvent(android.view.MotionEvent);
@@ -27451,7 +27458,7 @@
method public void onSetInteractiveAppNotificationEnabled(boolean);
method public abstract void onSetStreamVolume(@FloatRange(from=0.0, to=1.0) float);
method public abstract boolean onSetSurface(@Nullable android.view.Surface);
- method public void onSetTvMessageEnabled(@NonNull String, boolean);
+ method public void onSetTvMessageEnabled(int, boolean);
method public void onSurfaceChanged(int, int, int);
method public long onTimeShiftGetCurrentPosition();
method public long onTimeShiftGetStartPosition();
@@ -27501,17 +27508,17 @@
method @NonNull public android.net.Uri getChannelUri();
method @NonNull public java.util.List<android.media.tv.TvContentRating> getContentRatings();
method @NonNull public String getDescription();
- method @NonNull public long getEndPaddingMillis();
+ method public long getEndPaddingMillis();
method @NonNull public String getName();
method @Nullable public android.net.Uri getProgramUri();
- method @IntRange(from=0xffffffff) @NonNull public long getRecordingDurationMillis();
+ method @IntRange(from=0xffffffff) public long getRecordingDurationMillis();
method @NonNull public String getRecordingId();
- method @IntRange(from=0xffffffff) @NonNull public long getRecordingStartTimeMillis();
+ method @IntRange(from=0xffffffff) public long getRecordingStartTimeMillis();
method @Nullable public android.net.Uri getRecordingUri();
- method @NonNull public int getRepeatDays();
- method @IntRange(from=0) @NonNull public long getScheduledDurationMillis();
- method @IntRange(from=0) @NonNull public long getScheduledStartTimeMillis();
- method @NonNull public long getStartPaddingMillis();
+ method public int getRepeatDays();
+ method @IntRange(from=0) public long getScheduledDurationMillis();
+ method @IntRange(from=0) public long getScheduledStartTimeMillis();
+ method public long getStartPaddingMillis();
method public void setDescription(@NonNull String);
method public void setName(@NonNull String);
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -27594,7 +27601,7 @@
method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
method public void setStreamVolume(@FloatRange(from=0.0, to=1.0) float);
method public void setTimeShiftPositionCallback(@Nullable android.media.tv.TvView.TimeShiftPositionCallback);
- method public void setTvMessageEnabled(@NonNull String, boolean);
+ method public void setTvMessageEnabled(int, boolean);
method public void setZOrderMediaOverlay(boolean);
method public void setZOrderOnTop(boolean);
method public void timeShiftPause();
@@ -27635,7 +27642,7 @@
method public void onTrackSelected(String, int, String);
method public void onTracksChanged(String, java.util.List<android.media.tv.TvTrackInfo>);
method public void onTuned(@NonNull String, @NonNull android.net.Uri);
- method public void onTvMessage(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
+ method public void onTvMessage(@NonNull String, int, @NonNull android.os.Bundle);
method public void onVideoAvailable(String);
method public void onVideoSizeChanged(String, int, int);
method public void onVideoUnavailable(String, int);
@@ -27743,7 +27750,7 @@
ctor public TvInteractiveAppService.Session(@NonNull android.content.Context);
method public boolean isMediaViewEnabled();
method @CallSuper public void layoutSurface(int, int, int, int);
- method @CallSuper public void notifyAdBuffer(@NonNull android.media.tv.AdBuffer);
+ method @CallSuper public void notifyAdBufferReady(@NonNull android.media.tv.AdBuffer);
method @CallSuper public final void notifyBiInteractiveAppCreated(@NonNull android.net.Uri, @Nullable String);
method @CallSuper public void notifySessionStateChanged(int, int);
method @CallSuper public final void notifyTeletextAppStateChanged(int);
@@ -27795,7 +27802,7 @@
method public boolean onTrackballEvent(@NonNull android.view.MotionEvent);
method public void onTracksChanged(@NonNull java.util.List<android.media.tv.TvTrackInfo>);
method public void onTuned(@NonNull android.net.Uri);
- method public void onTvMessage(@NonNull String, @NonNull android.os.Bundle);
+ method public void onTvMessage(int, @NonNull android.os.Bundle);
method public void onTvRecordingInfo(@Nullable android.media.tv.TvRecordingInfo);
method public void onTvRecordingInfoList(@NonNull java.util.List<android.media.tv.TvRecordingInfo>);
method public void onVideoAvailable();
@@ -27817,7 +27824,7 @@
method @CallSuper public void requestTimeShiftMode();
method @CallSuper public void requestTrackInfoList();
method @CallSuper public void requestTvRecordingInfo(@NonNull String);
- method @CallSuper public void requestTvRecordingInfoList(@NonNull int);
+ method @CallSuper public void requestTvRecordingInfoList(int);
method @CallSuper public void sendPlaybackCommandRequest(@NonNull String, @Nullable android.os.Bundle);
method @CallSuper public void sendTimeShiftCommandRequest(@NonNull String, @Nullable android.os.Bundle);
method @CallSuper public void setMediaViewEnabled(boolean);
@@ -27859,7 +27866,7 @@
method public void notifyTimeShiftPlaybackParams(@NonNull android.media.PlaybackParams);
method public void notifyTimeShiftStartPositionChanged(@NonNull String, long);
method public void notifyTimeShiftStatusChanged(@NonNull String, int);
- method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
+ method public void notifyTvMessage(@NonNull int, @NonNull android.os.Bundle);
method public void onAttachedToWindow();
method public void onDetachedFromWindow();
method public void onLayout(boolean, int, int, int, int);
@@ -27916,7 +27923,7 @@
method public void onRequestTimeShiftMode(@NonNull String);
method public void onRequestTrackInfoList(@NonNull String);
method public void onRequestTvRecordingInfo(@NonNull String, @NonNull String);
- method public void onRequestTvRecordingInfoList(@NonNull String, @NonNull int);
+ method public void onRequestTvRecordingInfoList(@NonNull String, int);
method public void onSetTvRecordingInfo(@NonNull String, @NonNull String, @NonNull android.media.tv.TvRecordingInfo);
method public void onSetVideoBounds(@NonNull String, @NonNull android.graphics.Rect);
method public void onStateChanged(@NonNull String, int, int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fb5ee8d..765a17f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -33,6 +33,7 @@
field public static final String ADD_TRUSTED_DISPLAY = "android.permission.ADD_TRUSTED_DISPLAY";
field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
field public static final String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
+ field public static final String ALLOWLISTED_WRITE_DEVICE_CONFIG = "android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG";
field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
field public static final String ALLOW_PLACE_IN_MULTI_PANE_SETTINGS = "android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS";
field public static final String ALLOW_SLIPPERY_TOUCHES = "android.permission.ALLOW_SLIPPERY_TOUCHES";
@@ -287,6 +288,7 @@
field public static final String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO";
field public static final String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
field public static final String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
+ field public static final String READ_WRITE_SYNC_DISABLED_MODE_CONFIG = "android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG";
field public static final String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
field public static final String RECEIVE_BLUETOOTH_MAP = "android.permission.RECEIVE_BLUETOOTH_MAP";
field public static final String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE";
@@ -923,7 +925,7 @@
public class KeyguardManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public long addWeakEscrowToken(@NonNull byte[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.WeakEscrowTokenActivatedListener);
- method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.content.Intent createConfirmDeviceCredentialForRemoteValidationIntent(@NonNull android.app.StartLockscreenValidationRequest, @NonNull android.content.ComponentName, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence);
+ method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.content.Intent createConfirmDeviceCredentialForRemoteValidationIntent(@NonNull android.app.RemoteLockscreenValidationSession, @NonNull android.content.ComponentName, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence);
method public android.content.Intent createConfirmFactoryResetCredentialIntent(CharSequence, CharSequence, CharSequence);
method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public int getMinLockLength(boolean, int);
method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public boolean getPrivateNotificationsAllowed();
@@ -935,7 +937,7 @@
method @RequiresPermission(android.Manifest.permission.SHOW_KEYGUARD_MESSAGE) public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable CharSequence, @Nullable android.app.KeyguardManager.KeyguardDismissCallback);
method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean setLock(int, @NonNull byte[], int);
method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public void setPrivateNotificationsAllowed(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.StartLockscreenValidationRequest startRemoteLockscreenValidation();
+ method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.RemoteLockscreenValidationSession startRemoteLockscreenValidation();
method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean unregisterWeakEscrowTokenRemovedListener(@NonNull android.app.KeyguardManager.WeakEscrowTokenRemovedListener);
method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.RemoteLockscreenValidationResult validateRemoteLockscreen(@NonNull byte[]);
field public static final int PASSWORD = 0; // 0x0
@@ -1024,6 +1026,7 @@
field public static final int RESULT_GUESS_VALID = 1; // 0x1
field public static final int RESULT_LOCKOUT = 3; // 0x3
field public static final int RESULT_NO_REMAINING_ATTEMPTS = 4; // 0x4
+ field public static final int RESULT_SESSION_EXPIRED = 5; // 0x5
}
public static final class RemoteLockscreenValidationResult.Builder {
@@ -1033,6 +1036,23 @@
method @NonNull public android.app.RemoteLockscreenValidationResult.Builder setTimeoutMillis(long);
}
+ public final class RemoteLockscreenValidationSession implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getLockType();
+ method public int getRemainingAttempts();
+ method @NonNull public byte[] getSourcePublicKey();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.RemoteLockscreenValidationSession> CREATOR;
+ }
+
+ public static final class RemoteLockscreenValidationSession.Builder {
+ ctor public RemoteLockscreenValidationSession.Builder();
+ method @NonNull public android.app.RemoteLockscreenValidationSession build();
+ method @NonNull public android.app.RemoteLockscreenValidationSession.Builder setLockType(int);
+ method @NonNull public android.app.RemoteLockscreenValidationSession.Builder setRemainingAttempts(int);
+ method @NonNull public android.app.RemoteLockscreenValidationSession.Builder setSourcePublicKey(@NonNull byte[]);
+ }
+
public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
method public int describeContents();
@@ -1050,23 +1070,6 @@
method public void launchAssist(@Nullable android.os.Bundle);
}
- public final class StartLockscreenValidationRequest implements android.os.Parcelable {
- method public int describeContents();
- method public int getLockscreenUiType();
- method public int getRemainingAttempts();
- method @NonNull public byte[] getSourcePublicKey();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.StartLockscreenValidationRequest> CREATOR;
- }
-
- public static final class StartLockscreenValidationRequest.Builder {
- ctor public StartLockscreenValidationRequest.Builder();
- method @NonNull public android.app.StartLockscreenValidationRequest build();
- method @NonNull public android.app.StartLockscreenValidationRequest.Builder setLockscreenUiType(int);
- method @NonNull public android.app.StartLockscreenValidationRequest.Builder setRemainingAttempts(int);
- method @NonNull public android.app.StartLockscreenValidationRequest.Builder setSourcePublicKey(@NonNull byte[]);
- }
-
public class StatusBarManager {
method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarMode();
@@ -10158,6 +10161,8 @@
public abstract class SharedConnectivityService extends android.app.Service {
ctor public SharedConnectivityService();
+ method public static boolean areHotspotNetworksEnabledForService(@NonNull android.content.Context);
+ method public static boolean areKnownNetworksEnabledForService(@NonNull android.content.Context);
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onConnectHotspotNetwork(@NonNull android.net.wifi.sharedconnectivity.app.HotspotNetwork);
method public abstract void onConnectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
@@ -14611,7 +14616,6 @@
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int);
- method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) public android.os.UserHandle getSubscriptionUserHandle(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
method public void requestEmbeddedSubscriptionInfoListRefresh();
method public void requestEmbeddedSubscriptionInfoListRefresh(int);
@@ -14621,7 +14625,6 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
- method @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) public void setSubscriptionUserHandle(int, @Nullable android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(int, boolean);
field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 46651a9..14c53d1 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -5,6 +5,7 @@
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
+ field public static final String ALLOWLISTED_WRITE_DEVICE_CONFIG = "android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG";
field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
@@ -39,6 +40,7 @@
field public static final String QUERY_AUDIO_STATE = "android.permission.QUERY_AUDIO_STATE";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
+ field public static final String READ_WRITE_SYNC_DISABLED_MODE_CONFIG = "android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG";
field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
field public static final String REMAP_MODIFIER_KEYS = "android.permission.REMAP_MODIFIER_KEYS";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
@@ -1957,7 +1959,7 @@
}
public class TvView extends android.view.ViewGroup {
- method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
+ method public void notifyTvMessage(int, @NonNull android.os.Bundle);
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d810f05..90427cb 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.StopUserOnSwitch;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.PermissionMethod;
@@ -47,6 +48,8 @@
import com.android.internal.os.TimeoutRecord;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -991,4 +994,48 @@
*/
public abstract void logFgsApiEnd(int apiType, int uid, int pid);
+ /**
+ * The list of the events about the {@link android.media.projection.IMediaProjection} itself.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ MEDIA_PROJECTION_TOKEN_EVENT_CREATED,
+ MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED,
+ })
+ public @interface MediaProjectionTokenEvent{};
+
+ /**
+ * An instance of {@link android.media.projection.IMediaProjection} has been created
+ * by the system.
+ *
+ * @hide
+ */
+ public static final @MediaProjectionTokenEvent int MEDIA_PROJECTION_TOKEN_EVENT_CREATED = 0;
+
+ /**
+ * An instance of {@link android.media.projection.IMediaProjection} has been destroyed
+ * by the system.
+ *
+ * @hide
+ */
+ public static final @MediaProjectionTokenEvent int MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED = 1;
+
+ /**
+ * Called after the system created/destroyed a media projection for an app, if the user
+ * has granted the permission to start a media projection from this app.
+ *
+ * <p>This API is specifically for the use case of enforcing the FGS type
+ * {@code android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION},
+ * where the app who is starting this type of FGS must have been granted with the permission
+ * to start the projection via the {@link android.media.projection.MediaProjection} APIs.
+ *
+ * @param uid The uid of the app which the system created/destroyed a media projection for.
+ * @param projectionToken The {@link android.media.projection.IMediaProjection} token that
+ * the system created/destroyed.
+ * @param event The actual event happening to the given {@code projectionToken}.
+ */
+ public abstract void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
+ @MediaProjectionTokenEvent int event);
}
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index a7824a8..be012cf 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -59,6 +59,9 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.permission.PermissionCheckerManager;
+import android.provider.DeviceConfig;
+import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -147,6 +150,108 @@
public static final long FGS_TYPE_PERMISSION_CHANGE_ID = 254662522L;
/**
+ * The prefix for the feature flags of the permission enforcement.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX =
+ "fgs_type_perm_enforcement_flag_";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_DATA_SYNC =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "data_sync";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PLAYBACK =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "media_playback";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_PHONE_CALL},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_PHONE_CALL =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "phone_call";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_LOCATION =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "location";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_CONNECTED_DEVICE =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "connected_device";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PROJECTION =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "media_projection";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_CAMERA =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "camera";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MICROPHONE =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "microphone";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_HEALTH},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "health";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_REMOTE_MESSAGING =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "remote_messaging";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_SYSTEM_EXEMPTED =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "system_exempted";
+
+ /**
+ * The feature flag of the permission enforcement for
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE},
+ * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+ */
+ private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_SPECIAL_USE =
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "special_use";
+
+ /**
* The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST}.
*
* @hide
@@ -156,8 +261,10 @@
FOREGROUND_SERVICE_TYPE_MANIFEST,
FGS_TYPE_NONE_DEPRECATION_CHANGE_ID,
FGS_TYPE_NONE_DISABLED_CHANGE_ID,
- null,
- null
+ null /* allOfPermissions */,
+ null /* anyOfPermissions */,
+ null /* permissionEnforcementFlag */,
+ false /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -170,8 +277,10 @@
FOREGROUND_SERVICE_TYPE_NONE,
FGS_TYPE_NONE_DEPRECATION_CHANGE_ID,
FGS_TYPE_NONE_DISABLED_CHANGE_ID,
- null,
- null
+ null /* allOfPermissions */,
+ null /* anyOfPermissions */,
+ null /* permissionEnforcementFlag */,
+ false /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -187,7 +296,9 @@
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC)
}, true),
- null
+ null /* anyOfPermissions */,
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_DATA_SYNC /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -203,7 +314,9 @@
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK)
}, true),
- null
+ null /* anyOfPermissions */,
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PLAYBACK /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -221,7 +334,9 @@
}, true),
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.MANAGE_OWN_CALLS)
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_PHONE_CALL /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -240,7 +355,9 @@
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.ACCESS_COARSE_LOCATION),
new RegularPermission(Manifest.permission.ACCESS_FINE_LOCATION),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_LOCATION /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -268,7 +385,9 @@
new RegularPermission(Manifest.permission.UWB_RANGING),
new UsbDevicePermission(),
new UsbAccessoryPermission(),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_CONNECTED_DEVICE /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -284,7 +403,12 @@
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION)
}, true),
- null
+ new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
+ new RegularPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT),
+ new AppOpPermission(AppOpsManager.OP_PROJECT_MEDIA)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PROJECTION /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -303,7 +427,9 @@
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.CAMERA),
new RegularPermission(Manifest.permission.SYSTEM_CAMERA),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_CAMERA /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -326,7 +452,9 @@
new RegularPermission(Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT),
new RegularPermission(Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT),
new RegularPermission(Manifest.permission.RECORD_AUDIO),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_MICROPHONE /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -347,7 +475,9 @@
new RegularPermission(Manifest.permission.BODY_SENSORS),
new RegularPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE),
new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -363,7 +493,9 @@
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING)
}, true),
- null
+ null /* anyOfPermissions */,
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_REMOTE_MESSAGING /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -383,7 +515,9 @@
new RegularPermission(Manifest.permission.SCHEDULE_EXACT_ALARM),
new RegularPermission(Manifest.permission.USE_EXACT_ALARM),
new AppOpPermission(AppOpsManager.OP_ACTIVATE_VPN),
- }, false)
+ }, false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_SYSTEM_EXEMPTED /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -396,7 +530,10 @@
FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
- null /* no type specific permissions */, null /* no type specific permissions */
+ null /* allOfPermissions */,
+ null /* anyOfPermissions */,
+ null /* permissionEnforcementFlag */,
+ false /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -412,7 +549,9 @@
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_FILE_MANAGEMENT)
}, true),
- null
+ null /* anyOfPermissions */,
+ null /* permissionEnforcementFlag */,
+ false /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -428,7 +567,9 @@
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE)
}, true),
- null
+ null /* anyOfPermissions */,
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_SPECIAL_USE /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */
);
/**
@@ -512,6 +653,14 @@
@NonNull String packageName, int callerUid, int callerPid, boolean allowWhileInUse,
@NonNull ForegroundServiceTypePolicyInfo policy);
+ /**
+ * Run the given {@code policyFunctor} on the matching policy, if the flag is known
+ * to the policy.
+ *
+ * @hide
+ */
+ public abstract void updatePermissionEnforcementFlagIfNecessary(@NonNull String flag);
+
@GuardedBy("sLock")
private static ForegroundServiceTypePolicy sDefaultForegroundServiceTypePolicy = null;
@@ -575,11 +724,31 @@
final @Nullable ForegroundServiceTypePermissions mAnyOfPermissions;
/**
+ * A permission enforcement flag, unlike the {@link #FGS_TYPE_PERMISSION_CHANGE_ID},
+ * here it applies to all apps using this FGS type.
+ */
+ final @Nullable String mPermissionEnforcementFlag;
+
+ /**
+ * The default value to {@link #mPermissionEnforcementFlag}.
+ */
+ final boolean mPermissionEnforcementFlagDefaultValue;
+
+ /**
* A customized check for the permissions.
*/
@Nullable ForegroundServiceTypePermission mCustomPermission;
/**
+ * The value of the permission enforcement flag, will be updated by the system.
+ * If the value is {@code false}, the FGS permission check will be ignored.
+ *
+ * <p>This value could be updated via the DeviceConfig flag specified
+ * in the {@link #mPermissionEnforcementFlag}.</p>
+ */
+ volatile boolean mPermissionEnforcementFlagValue;
+
+ /**
* Not a real change id, but a place holder.
*/
private static final long INVALID_CHANGE_ID = 0L;
@@ -599,12 +768,17 @@
public ForegroundServiceTypePolicyInfo(@ForegroundServiceType int type,
long deprecationChangeId, long disabledChangeId,
@Nullable ForegroundServiceTypePermissions allOfPermissions,
- @Nullable ForegroundServiceTypePermissions anyOfPermissions) {
+ @Nullable ForegroundServiceTypePermissions anyOfPermissions,
+ @Nullable String permissionEnforcementFlag,
+ boolean permissionEnforcementFlagDefaultValue) {
mType = type;
mDeprecationChangeId = deprecationChangeId;
mDisabledChangeId = disabledChangeId;
mAllOfPermissions = allOfPermissions;
mAnyOfPermissions = anyOfPermissions;
+ mPermissionEnforcementFlag = permissionEnforcementFlag;
+ mPermissionEnforcementFlagDefaultValue = permissionEnforcementFlagDefaultValue;
+ mPermissionEnforcementFlagValue = permissionEnforcementFlagDefaultValue;
}
/**
@@ -650,6 +824,17 @@
return sb;
}
+ private void updatePermissionEnforcementFlagIfNecessary(@NonNull String flagName) {
+ if (mPermissionEnforcementFlag == null
+ || !TextUtils.equals(flagName, mPermissionEnforcementFlag)) {
+ return;
+ }
+ mPermissionEnforcementFlagValue = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ mPermissionEnforcementFlag,
+ mPermissionEnforcementFlagDefaultValue);
+ }
+
/**
* @hide
*/
@@ -745,6 +930,15 @@
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
platformCompat.clearOverrideForTest(changeId, packageName);
}
+
+ /**
+ * For test only.
+ *
+ * @return The permission enforcement flag.
+ */
+ public @Nullable String getPermissionEnforcementFlagForTest() {
+ return mPermissionEnforcementFlag;
+ }
}
/**
@@ -991,6 +1185,12 @@
new SparseArray<>();
/**
+ * The map between permission enforcement flag to its permission policy info.
+ */
+ private final ArrayMap<String, ForegroundServiceTypePolicyInfo>
+ mPermissionEnforcementToPolicyInfoMap = new ArrayMap<>();
+
+ /**
* Constructor
*/
public DefaultForegroundServiceTypePolicy() {
@@ -1022,10 +1222,15 @@
FGS_TYPE_POLICY_SYSTEM_EXEMPTED);
mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
FGS_TYPE_POLICY_SHORT_SERVICE);
- mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_FILE_MANAGEMENT,
- FGS_TYPE_POLICY_FILE_MANAGEMENT);
+ // TODO (b/271950506): revisit it in the next release.
+ // Hide the file management type for now. If anyone uses it, will default to "none".
mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SPECIAL_USE,
FGS_TYPE_POLICY_SPECIAL_USE);
+ for (int i = 0, size = mForegroundServiceTypePolicies.size(); i < size; i++) {
+ final ForegroundServiceTypePolicyInfo info =
+ mForegroundServiceTypePolicies.valueAt(i);
+ mPermissionEnforcementToPolicyInfoMap.put(info.mPermissionEnforcementFlag, info);
+ }
}
@Override
@@ -1078,8 +1283,8 @@
}
}
if (permissionResult != PERMISSION_GRANTED) {
- return (CompatChanges.isChangeEnabled(
- FGS_TYPE_PERMISSION_CHANGE_ID, callerUid))
+ return policy.mPermissionEnforcementFlagValue
+ && (CompatChanges.isChangeEnabled(FGS_TYPE_PERMISSION_CHANGE_ID, callerUid))
? FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED
: FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE;
}
@@ -1089,5 +1294,14 @@
}
return FGS_TYPE_POLICY_CHECK_OK;
}
+
+ @Override
+ public void updatePermissionEnforcementFlagIfNecessary(@NonNull String flagName) {
+ final ForegroundServiceTypePolicyInfo info =
+ mPermissionEnforcementToPolicyInfoMap.get(flagName);
+ if (info != null) {
+ info.updatePermissionEnforcementFlagIfNecessary(flagName);
+ }
+ }
}
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index be0d1c9..0ef8e92 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -146,13 +146,13 @@
public static final String EXTRA_CHECKBOX_LABEL = "android.app.extra.CHECKBOX_LABEL";
/**
- * A {@link StartLockscreenValidationRequest} extra to be sent along with
+ * A {@link RemoteLockscreenValidationSession} extra to be sent along with
* {@link #ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL} containing the data needed to prompt for
* a remote device's lock screen.
* @hide
*/
- public static final String EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST =
- "android.app.extra.START_LOCKSCREEN_VALIDATION_REQUEST";
+ public static final String EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION =
+ "android.app.extra.REMOTE_LOCKSCREEN_VALIDATION_SESSION";
/**
* Result code returned by the activity started by
@@ -359,8 +359,7 @@
/**
* Get an Intent to launch an activity to prompt the user to confirm the
* credentials (pin, pattern or password) of a remote device.
- * @param startLockscreenValidationRequest contains information necessary to start remote device
- * credential validation.
+ * @param session contains information necessary to start remote device credential validation.
* @param remoteLockscreenValidationServiceComponent
* the {@link ComponentName} of the implementation of
* {@link android.service.remotelockscreenvalidation.RemoteLockscreenValidationService}
@@ -376,15 +375,14 @@
@RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN)
@NonNull
public Intent createConfirmDeviceCredentialForRemoteValidationIntent(
- @NonNull StartLockscreenValidationRequest startLockscreenValidationRequest,
+ @NonNull RemoteLockscreenValidationSession session,
@NonNull ComponentName remoteLockscreenValidationServiceComponent,
@Nullable CharSequence title,
@Nullable CharSequence description,
@Nullable CharSequence checkboxLabel,
@Nullable CharSequence alternateButtonLabel) {
Intent intent = new Intent(ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL)
- .putExtra(
- EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST, startLockscreenValidationRequest)
+ .putExtra(EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION, session)
.putExtra(Intent.EXTRA_COMPONENT_NAME, remoteLockscreenValidationServiceComponent)
.putExtra(EXTRA_TITLE, title)
.putExtra(EXTRA_DESCRIPTION, description)
@@ -1157,7 +1155,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN)
@NonNull
- public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+ public RemoteLockscreenValidationSession startRemoteLockscreenValidation() {
return mLockPatternUtils.startRemoteLockscreenValidation();
}
@@ -1165,11 +1163,10 @@
* Verifies credentials guess from a remote device.
*
* <p>Secret must be encrypted using {@code SecureBox} library
- * with public key from {@code StartLockscreenValidationRequest}
+ * with public key from {@code RemoteLockscreenValidationSession}
* and header set to {@code "encrypted_remote_credentials"} in UTF-8 encoding.
*
- * @throws IllegalStateException if there is no active lock screen validation session or
- * there was a decryption error.
+ * @throws IllegalStateException if there was a decryption error.
*
* @hide
*/
diff --git a/core/java/android/app/RemoteLockscreenValidationResult.java b/core/java/android/app/RemoteLockscreenValidationResult.java
index 0245f8c..bbb3567 100644
--- a/core/java/android/app/RemoteLockscreenValidationResult.java
+++ b/core/java/android/app/RemoteLockscreenValidationResult.java
@@ -55,10 +55,16 @@
*/
public static final int RESULT_NO_REMAINING_ATTEMPTS = 4;
+ /**
+ * New lockscreen validation session is required to verify guess.
+ */
+ public static final int RESULT_SESSION_EXPIRED = 5;
+
@IntDef({RESULT_GUESS_VALID,
RESULT_GUESS_INVALID,
RESULT_LOCKOUT,
- RESULT_NO_REMAINING_ATTEMPTS})
+ RESULT_NO_REMAINING_ATTEMPTS,
+ RESULT_SESSION_EXPIRED})
@Retention(RetentionPolicy.SOURCE)
@interface ResultCode {}
diff --git a/core/java/android/app/StartLockscreenValidationRequest.aidl b/core/java/android/app/RemoteLockscreenValidationSession.aidl
similarity index 93%
rename from core/java/android/app/StartLockscreenValidationRequest.aidl
rename to core/java/android/app/RemoteLockscreenValidationSession.aidl
index 367dfee..edc8d56 100644
--- a/core/java/android/app/StartLockscreenValidationRequest.aidl
+++ b/core/java/android/app/RemoteLockscreenValidationSession.aidl
@@ -17,4 +17,4 @@
package android.app;
/** {@hide} */
-parcelable StartLockscreenValidationRequest;
+parcelable RemoteLockscreenValidationSession;
diff --git a/core/java/android/app/StartLockscreenValidationRequest.java b/core/java/android/app/RemoteLockscreenValidationSession.java
similarity index 69%
rename from core/java/android/app/StartLockscreenValidationRequest.java
rename to core/java/android/app/RemoteLockscreenValidationSession.java
index e818195..c6592e3 100644
--- a/core/java/android/app/StartLockscreenValidationRequest.java
+++ b/core/java/android/app/RemoteLockscreenValidationSession.java
@@ -30,44 +30,45 @@
* @hide
*/
@SystemApi
-public final class StartLockscreenValidationRequest implements Parcelable {
+public final class RemoteLockscreenValidationSession implements Parcelable {
@LockTypes
- private int mLockscreenUiType;
+ private int mLockType;
private byte[] mSourcePublicKey;
private int mRemainingAttempts;
- public static final @NonNull Parcelable.Creator<StartLockscreenValidationRequest> CREATOR = new
- Parcelable.Creator<StartLockscreenValidationRequest>() {
+ public static final @NonNull Parcelable.Creator<RemoteLockscreenValidationSession> CREATOR = new
+ Parcelable.Creator<RemoteLockscreenValidationSession>() {
@Override
- public StartLockscreenValidationRequest createFromParcel(Parcel source) {
- return new StartLockscreenValidationRequest(source);
+ public RemoteLockscreenValidationSession createFromParcel(Parcel source) {
+ return new RemoteLockscreenValidationSession(source);
}
@Override
- public StartLockscreenValidationRequest[] newArray(int size) {
- return new StartLockscreenValidationRequest[size];
+ public RemoteLockscreenValidationSession[] newArray(int size) {
+ return new RemoteLockscreenValidationSession[size];
}
};
/**
- * Builder for {@code StartLockscreenValidationRequest}
+ * Builder for {@code RemoteLockscreenValidationSession}
*/
public static final class Builder {
- private StartLockscreenValidationRequest mInstance = new StartLockscreenValidationRequest();
+ private RemoteLockscreenValidationSession mInstance =
+ new RemoteLockscreenValidationSession();
/**
* Sets UI type.
* Default value is {@code LockTypes.PASSWORD}
*
- * @param lockscreenUiType The UI format
+ * @param lockType The UI format
* @return This builder.
*/
- public @NonNull Builder setLockscreenUiType(@LockTypes int lockscreenUiType) {
- mInstance.mLockscreenUiType = lockscreenUiType;
+ public @NonNull Builder setLockType(@LockTypes int lockType) {
+ mInstance.mLockType = lockType;
return this;
}
@@ -92,11 +93,11 @@
}
/**
- * Creates {@code StartLockscreenValidationRequest}
+ * Creates {@code RemoteLockscreenValidationSession}
*
* @throws NullPointerException if required fields are not set.
*/
- public @NonNull StartLockscreenValidationRequest build() {
+ public @NonNull RemoteLockscreenValidationSession build() {
Objects.requireNonNull(mInstance.mSourcePublicKey);
return mInstance;
}
@@ -105,8 +106,8 @@
/**
* Specifies lock screen credential type.
*/
- public @LockTypes int getLockscreenUiType() {
- return mLockscreenUiType;
+ public @LockTypes int getLockType() {
+ return mLockType;
}
/**
@@ -127,16 +128,16 @@
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
- out.writeInt(mLockscreenUiType);
+ out.writeInt(mLockType);
out.writeByteArray(mSourcePublicKey);
out.writeInt(mRemainingAttempts);
}
- private StartLockscreenValidationRequest() {
+ private RemoteLockscreenValidationSession() {
}
- private StartLockscreenValidationRequest(Parcel in) {
- mLockscreenUiType = in.readInt();
+ private RemoteLockscreenValidationSession(Parcel in) {
+ mLockType = in.readInt();
mSourcePublicKey = in.createByteArray();
mRemainingAttempts = in.readInt();
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 4532661..4b3eb3a 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -1392,10 +1392,6 @@
return sql.replaceAll("[\\s]*\\n+[\\s]*", " ");
}
- void clearPreparedStatementCache() {
- mPreparedStatementCache.evictAll();
- }
-
/**
* Holder type for a prepared statement.
*
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 6023d66..069c264 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -1126,16 +1126,6 @@
mConnectionWaiterPool = waiter;
}
- void clearAcquiredConnectionsPreparedStatementCache() {
- synchronized (mLock) {
- if (!mAcquiredConnections.isEmpty()) {
- for (SQLiteConnection connection : mAcquiredConnections.keySet()) {
- connection.clearPreparedStatementCache();
- }
- }
- }
- }
-
/**
* Dumps debugging information about this connection pool.
*
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index db898c3..c08294f 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2088,12 +2088,10 @@
try (SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs)) {
return statement.executeUpdateDelete();
} finally {
- // If schema was updated, close non-primary connections and clear prepared
- // statement caches of active connections, otherwise they might have outdated
- // schema information.
+ // If schema was updated, close non-primary connections, otherwise they might
+ // have outdated schema information
if (statementType == DatabaseUtils.STATEMENT_DDL) {
mConnectionPoolLocked.closeAvailableNonPrimaryConnectionsAndLogExceptions();
- mConnectionPoolLocked.clearAcquiredConnectionsPreparedStatementCache();
}
}
} finally {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index fbc0184..6cd4a2e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1189,8 +1189,8 @@
PackageManager packageManager = context.getPackageManager();
try {
- return packageManager.getProperty(context.getOpPackageName(),
- PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT).getBoolean();
+ return packageManager.getProperty(PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT,
+ context.getOpPackageName()).getBoolean();
} catch (PackageManager.NameNotFoundException e) {
// No such property
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index cb678b9..5a48176 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -26,7 +26,6 @@
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.SurfaceUtils;
-import android.util.Log;
import android.util.Range;
import android.util.Size;
import android.util.SparseIntArray;
@@ -2019,7 +2018,7 @@
/**
* @hide
*/
- public static final int HAL_DATASPACE_HEIF = 0x1003;
+ public static final int HAL_DATASPACE_HEIF = 0x1004;
/**
* @hide
*/
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index d14abfd..d2f9ff0 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3396,8 +3396,10 @@
public static final int MATCH_ALL_APN_SET_ID = -1;
/**
- * A unique carrier id associated with this APN
- * {@see TelephonyManager#getSimCarrierId()}
+ * A unique carrier id associated with this APN {@link TelephonyManager#getSimCarrierId()}
+ * In case of matching carrier information, this should be used by default instead of
+ * those fields of {@link #MCC}, {@link #MNC}, {@link #NUMERIC}, {@link #MVNO_TYPE},
+ * {@link #MVNO_MATCH_DATA}, etc.
* <p>Type: STRING</p>
*/
public static final String CARRIER_ID = "carrier_id";
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 0ef8bb64..e81ca1a 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -28,6 +28,7 @@
import android.content.IntentSender;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArrayMap;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
@@ -434,14 +435,14 @@
* one value for a field or set an authentication intent.
*/
public static final class Builder {
- private ArrayList<AutofillId> mFieldIds;
- private ArrayList<AutofillValue> mFieldValues;
- private ArrayList<RemoteViews> mFieldPresentations;
- private ArrayList<RemoteViews> mFieldDialogPresentations;
- private ArrayList<InlinePresentation> mFieldInlinePresentations;
- private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
- private ArrayList<DatasetFieldFilter> mFieldFilters;
- private ArrayList<String> mAutofillDatatypes;
+ private ArrayList<AutofillId> mFieldIds = new ArrayList<>();
+ private ArrayList<AutofillValue> mFieldValues = new ArrayList();
+ private ArrayList<RemoteViews> mFieldPresentations = new ArrayList();
+ private ArrayList<RemoteViews> mFieldDialogPresentations = new ArrayList();
+ private ArrayList<InlinePresentation> mFieldInlinePresentations = new ArrayList();
+ private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations = new ArrayList();
+ private ArrayList<DatasetFieldFilter> mFieldFilters = new ArrayList();
+ private ArrayList<String> mAutofillDatatypes = new ArrayList();
@Nullable private ClipData mFieldContent;
private RemoteViews mPresentation;
private RemoteViews mDialogPresentation;
@@ -452,6 +453,15 @@
@Nullable private String mId;
/**
+ * Usually, a field will be associated with a single autofill id and/or datatype.
+ * There could be null field value corresponding to different autofill ids or datatye
+ * values, but the implementation is ok with duplicating that information.
+ * This map is just for the purpose of optimization, to reduce the size of the pelled data
+ * over the binder transaction.
+ */
+ private ArrayMap<Field, Integer> mFieldToIndexdMap = new ArrayMap<>();
+
+ /**
* Creates a new builder.
*
* @param presentation The presentation used to visualize this dataset.
@@ -1051,29 +1061,40 @@
*/
public @NonNull Builder setField(@NonNull AutofillId id, @Nullable Field field) {
throwIfDestroyed();
+
+ if (mFieldToIndexdMap.containsKey(field)) {
+ int index = mFieldToIndexdMap.get(field);
+ if (mFieldIds.get(index) == null) {
+ mFieldIds.set(index, id);
+ return this;
+ }
+ // if the Autofill Id is already set, ignore and proceed as if setting in a new
+ // value.
+ }
+ int index;
if (field == null) {
- setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
+ index = setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
} else {
final DatasetFieldFilter filter = field.getDatasetFieldFilter();
final Presentations presentations = field.getPresentations();
if (presentations == null) {
- setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
+ index = setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
filter, null);
} else {
- setLifeTheUniverseAndEverything(id, field.getValue(),
+ index = setLifeTheUniverseAndEverything(id, field.getValue(),
presentations.getMenuPresentation(),
presentations.getInlinePresentation(),
presentations.getInlineTooltipPresentation(), filter,
presentations.getDialogPresentation());
}
}
+ mFieldToIndexdMap.put(field, index);
return this;
}
/**
- * Adds a field to this Dataset with a specific type and no
- * AutofillId. This is used to send back Field information
- * when Autofilling with platform detections is on.
+ * Adds a field to this Dataset with a specific type. This is used to send back Field
+ * information when Autofilling with platform detections is on.
* Platform detections are on when receiving a populated list from
* FillRequest#getHints().
*
@@ -1086,9 +1107,6 @@
* has two credential pairs, then two Datasets should be created,
* and so on.
*
- * Using this will remove any data populated with
- * setField(@NonNull AutofillId id, @Nullable Field field).
- *
* @param hint An autofill hint returned from {@link
* FillRequest#getHints()}.
*
@@ -1102,19 +1120,29 @@
public @NonNull Dataset.Builder setField(@NonNull String hint, @NonNull Field field) {
throwIfDestroyed();
+ if (mFieldToIndexdMap.containsKey(field)) {
+ int index = mFieldToIndexdMap.get(field);
+ if (mAutofillDatatypes.get(index) == null) {
+ mAutofillDatatypes.set(index, hint);
+ return this;
+ }
+ // if the hint is already set, ignore and proceed as if setting in a new hint.
+ }
+
+ int index;
final DatasetFieldFilter filter = field.getDatasetFieldFilter();
final Presentations presentations = field.getPresentations();
if (presentations == null) {
- setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null,
+ index = setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null,
filter, null);
} else {
- setLifeTheUniverseAndEverything(hint, field.getValue(),
+ index = setLifeTheUniverseAndEverything(hint, field.getValue(),
presentations.getMenuPresentation(),
presentations.getInlinePresentation(),
presentations.getInlineTooltipPresentation(), filter,
presentations.getDialogPresentation());
}
-
+ mFieldToIndexdMap.put(field, index);
return this;
}
@@ -1172,67 +1200,64 @@
return this;
}
- private void setLifeTheUniverseAndEverything(String datatype,
+ /** Returns the index at which this id was modified or inserted */
+ private int setLifeTheUniverseAndEverything(@NonNull String datatype,
@Nullable AutofillValue value,
@Nullable RemoteViews presentation,
@Nullable InlinePresentation inlinePresentation,
@Nullable InlinePresentation tooltip,
@Nullable DatasetFieldFilter filter,
@Nullable RemoteViews dialogPresentation) {
- if (mAutofillDatatypes == null) {
- mFieldValues = new ArrayList<>();
- mFieldPresentations = new ArrayList<>();
- mFieldDialogPresentations = new ArrayList<>();
- mFieldInlinePresentations = new ArrayList<>();
- mFieldInlineTooltipPresentations = new ArrayList<>();
- mFieldFilters = new ArrayList<>();
- mAutofillDatatypes = new ArrayList<>();
- mFieldIds = null;
+ Objects.requireNonNull(datatype, "datatype cannot be null");
+ final int existingIdx = mAutofillDatatypes.indexOf(datatype);
+ if (existingIdx >= 0) {
+ mAutofillDatatypes.add(datatype);
+ mFieldValues.set(existingIdx, value);
+ mFieldPresentations.set(existingIdx, presentation);
+ mFieldDialogPresentations.set(existingIdx, dialogPresentation);
+ mFieldInlinePresentations.set(existingIdx, inlinePresentation);
+ mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
+ mFieldFilters.set(existingIdx, filter);
+ return existingIdx;
}
+ mFieldIds.add(null);
+ mAutofillDatatypes.add(datatype);
mFieldValues.add(value);
mFieldPresentations.add(presentation);
mFieldDialogPresentations.add(dialogPresentation);
mFieldInlinePresentations.add(inlinePresentation);
mFieldInlineTooltipPresentations.add(tooltip);
mFieldFilters.add(filter);
- mAutofillDatatypes.add(datatype);
+ return mFieldIds.size() - 1;
}
- private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
+ /** Returns the index at which this id was modified or inserted */
+ private int setLifeTheUniverseAndEverything(@NonNull AutofillId id,
@Nullable AutofillValue value, @Nullable RemoteViews presentation,
@Nullable InlinePresentation inlinePresentation,
@Nullable InlinePresentation tooltip,
@Nullable DatasetFieldFilter filter,
@Nullable RemoteViews dialogPresentation) {
Objects.requireNonNull(id, "id cannot be null");
- if (mFieldIds != null) {
- final int existingIdx = mFieldIds.indexOf(id);
- if (existingIdx >= 0) {
- mFieldValues.set(existingIdx, value);
- mFieldPresentations.set(existingIdx, presentation);
- mFieldDialogPresentations.set(existingIdx, dialogPresentation);
- mFieldInlinePresentations.set(existingIdx, inlinePresentation);
- mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
- mFieldFilters.set(existingIdx, filter);
- return;
- }
- } else {
- mFieldIds = new ArrayList<>();
- mFieldValues = new ArrayList<>();
- mFieldPresentations = new ArrayList<>();
- mFieldDialogPresentations = new ArrayList<>();
- mFieldInlinePresentations = new ArrayList<>();
- mFieldInlineTooltipPresentations = new ArrayList<>();
- mFieldFilters = new ArrayList<>();
- mAutofillDatatypes = null;
+ final int existingIdx = mFieldIds.indexOf(id);
+ if (existingIdx >= 0) {
+ mFieldValues.set(existingIdx, value);
+ mFieldPresentations.set(existingIdx, presentation);
+ mFieldDialogPresentations.set(existingIdx, dialogPresentation);
+ mFieldInlinePresentations.set(existingIdx, inlinePresentation);
+ mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
+ mFieldFilters.set(existingIdx, filter);
+ return existingIdx;
}
mFieldIds.add(id);
+ mAutofillDatatypes.add(null);
mFieldValues.add(value);
mFieldPresentations.add(presentation);
mFieldDialogPresentations.add(dialogPresentation);
mFieldInlinePresentations.add(inlinePresentation);
mFieldInlineTooltipPresentations.add(tooltip);
mFieldFilters.add(filter);
+ return mFieldIds.size() - 1;
}
/**
@@ -1249,11 +1274,12 @@
throwIfDestroyed();
mDestroyed = true;
if (mFieldIds == null && mAutofillDatatypes == null) {
- throw new IllegalStateException("at least one value must be set");
+ throw new IllegalStateException("at least one of field or datatype must be set");
}
if (mFieldIds != null && mAutofillDatatypes != null) {
- if (mFieldIds.size() > 0 && mAutofillDatatypes.size() > 0) {
- throw new IllegalStateException("both field and datatype were populated");
+ if (mFieldIds.size() == 0 && mAutofillDatatypes.size() == 0) {
+ throw new IllegalStateException(
+ "at least one of field or datatype must be set");
}
}
if (mFieldContent != null) {
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index 9cd5aa0..3190c69 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -328,7 +328,7 @@
@NonNull Context context,
@UserIdInt int userId,
boolean disableSystemAppVerificationForTests,
- Set<ServiceInfo> enabledServices) {
+ Set<ComponentName> enabledServices) {
requireNonNull(context, "context must not be null");
final List<CredentialProviderInfo> providerInfos = new ArrayList<>();
@@ -342,7 +342,7 @@
si,
/* isSystemProvider= */ true,
disableSystemAppVerificationForTests,
- enabledServices.contains(si));
+ enabledServices.contains(si.getComponentName()));
if (cpi.isSystemProvider()) {
providerInfos.add(cpi);
} else {
@@ -371,31 +371,6 @@
}
/**
- * Returns a valid credential provider that has the given package name. Returns null if no
- * match is found.
- */
- @Nullable
- public static CredentialProviderInfo getCredentialProviderFromPackageName(
- @NonNull Context context,
- int userId,
- @NonNull String packageName,
- int providerFilter,
- @NonNull Set<ServiceInfo> enabledServices) {
- requireNonNull(context, "context must not be null");
- requireNonNull(packageName, "package name must not be null");
- requireNonNull(enabledServices, "enabledServices must not be null");
-
- for (CredentialProviderInfo credentialProviderInfo : getCredentialProviderServices(context,
- userId, providerFilter, enabledServices)) {
- if (credentialProviderInfo.getServiceInfo()
- .packageName.equals(packageName)) {
- return credentialProviderInfo;
- }
- }
- return null;
- }
-
- /**
* Returns the valid credential provider services available for the user with the given {@code
* userId}.
*/
@@ -404,7 +379,7 @@
@NonNull Context context,
int userId,
int providerFilter,
- Set<ServiceInfo> enabledServices) {
+ Set<ComponentName> enabledServices) {
requireNonNull(context, "context must not be null");
// Get the device policy.
@@ -433,7 +408,7 @@
@NonNull Context context,
int userId,
int providerFilter,
- Set<ServiceInfo> enabledServices) {
+ Set<ComponentName> enabledServices) {
requireNonNull(context, "context must not be null");
// Get the device policy.
@@ -539,7 +514,7 @@
@NonNull Context context,
@UserIdInt int userId,
boolean disableSystemAppVerificationForTests,
- Set<ServiceInfo> enabledServices) {
+ Set<ComponentName> enabledServices) {
final List<CredentialProviderInfo> services = new ArrayList<>();
final List<ResolveInfo> resolveInfos =
context.getPackageManager()
@@ -549,6 +524,11 @@
userId);
for (ResolveInfo resolveInfo : resolveInfos) {
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (serviceInfo == null) {
+ Log.i(TAG, "No serviceInfo found for resolveInfo so skipping this provider");
+ continue;
+ }
+
try {
CredentialProviderInfo cpi =
CredentialProviderInfoFactory.create(
@@ -556,7 +536,7 @@
serviceInfo,
/* isSystemProvider= */ false,
disableSystemAppVerificationForTests,
- enabledServices.contains(serviceInfo));
+ enabledServices.contains(serviceInfo.getComponentName()));
if (!cpi.isSystemProvider()) {
services.add(cpi);
}
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index a2fa139..a389223 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -58,11 +58,13 @@
setTitle(title);
}
- final Bundle extras = getIntent().getExtras();
- mCallback = (DreamService.DreamActivityCallbacks) extras.getBinder(EXTRA_CALLBACK);
-
- if (mCallback != null) {
+ final Object callback = getIntent().getExtras().getBinder(EXTRA_CALLBACK);
+ if (callback instanceof DreamService.DreamActivityCallbacks) {
+ mCallback = (DreamService.DreamActivityCallbacks) callback;
mCallback.onActivityCreated(this);
+ } else {
+ mCallback = null;
+ finishAndRemoveTask();
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ef76ce3..800fc97 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -163,6 +163,7 @@
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
import android.widget.Checkable;
+import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ScrollBarDrawable;
import android.window.OnBackInvokedDispatcher;
@@ -10356,7 +10357,7 @@
final AutofillManager afm = getAutofillManager();
// keep default behavior
if (afm == null) return false;
- return afm.isMatchingAutofillableHeuristics(this);
+ return afm.isMatchingAutofillableHeuristicsForNotImportantViews(this);
}
private boolean isAutofillable() {
@@ -10372,26 +10373,47 @@
return false;
}
+ // Experiment imeAction heuristic on important views. If the important view doesn't pass
+ // heuristic check, also check augmented autofill in case augmented autofill is enabled
+ // for the activity
+ // TODO: refactor to have both important views and not important views use the same
+ // heuristic check
+ if (isImportantForAutofill()
+ && afm.isTriggerFillRequestOnFilteredImportantViewsEnabled()
+ && this instanceof EditText
+ && !afm.isPassingImeActionCheck((EditText) this)
+ && !notifyAugmentedAutofillIfNeeded(afm)) {
+ // TODO: add a log to indicate what has filtered out the view
+ return false;
+ }
+
if (!isImportantForAutofill()) {
// If view matches heuristics and is not denied, it will be treated same as view that's
// important for autofill
- if (afm.isMatchingAutofillableHeuristics(this)
+ if (afm.isMatchingAutofillableHeuristicsForNotImportantViews(this)
&& !afm.isActivityDeniedForAutofillForUnimportantView()) {
return getAutofillViewId() > LAST_APP_AUTOFILL_ID;
}
// View is not important for "regular" autofill, so we must check if Augmented Autofill
// is enabled for the activity
- final AutofillOptions options = mContext.getAutofillOptions();
- if (options == null || !options.isAugmentedAutofillEnabled(mContext)) {
+ if (!notifyAugmentedAutofillIfNeeded(afm)){
return false;
}
-
- afm.notifyViewEnteredForAugmentedAutofill(this);
}
return getAutofillViewId() > LAST_APP_AUTOFILL_ID;
}
+ /** @hide **/
+ public boolean notifyAugmentedAutofillIfNeeded(AutofillManager afm) {
+ final AutofillOptions options = mContext.getAutofillOptions();
+ if (options == null || !options.isAugmentedAutofillEnabled(mContext)) {
+ return false;
+ }
+ afm.notifyViewEnteredForAugmentedAutofill(this);
+ return true;
+ }
+
/** @hide */
public boolean canNotifyAutofillEnterExitEvent() {
return isAutofillable() && isAttachedToWindow();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a8b4da1..24dcb69 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5718,7 +5718,7 @@
private static final int MSG_WINDOW_TOUCH_MODE_CHANGED = 34;
private static final int MSG_KEEP_CLEAR_RECTS_CHANGED = 35;
private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36;
-
+ private static final int MSG_PAUSED_FOR_SYNC_TIMEOUT = 37;
final class ViewRootHandler extends Handler {
@Override
@@ -6006,6 +6006,11 @@
case MSG_REQUEST_SCROLL_CAPTURE:
handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
break;
+ case MSG_PAUSED_FOR_SYNC_TIMEOUT:
+ Log.e(mTag, "Timedout waiting to unpause for sync");
+ mNumPausedForSync = 0;
+ scheduleTraversals();
+ break;
}
}
}
@@ -11584,9 +11589,16 @@
mActiveSurfaceSyncGroup = new SurfaceSyncGroup(mTag);
mActiveSurfaceSyncGroup.setAddedToSyncListener(() -> {
Runnable runnable = () -> {
- mNumPausedForSync--;
- if (!mIsInTraversal && mNumPausedForSync == 0) {
- scheduleTraversals();
+ // Check if it's already 0 because the timeout could have reset the count to
+ // 0 and we don't want to go negative.
+ if (mNumPausedForSync > 0) {
+ mNumPausedForSync--;
+ }
+ if (mNumPausedForSync == 0) {
+ mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
+ if (!mIsInTraversal) {
+ scheduleTraversals();
+ }
}
};
@@ -11613,6 +11625,8 @@
}
mNumPausedForSync++;
+ mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT, 1000);
return mActiveSurfaceSyncGroup;
};
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 6d78e60..7da69e7 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -156,6 +156,14 @@
"trigger_fill_request_on_unimportant_view";
/**
+ * Whether to apply heuristic check on important views.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_FILTERED_IMPORTANT_VIEWS =
+ "trigger_fill_request_on_filtered_important_views";
+
+ /**
* Continas imeAction ids that is irrelevant for autofill. For example, ime_action_search. We
* use this to avoid trigger fill request on unimportant views.
*
@@ -167,6 +175,14 @@
@SuppressLint("IntentName")
public static final String DEVICE_CONFIG_NON_AUTOFILLABLE_IME_ACTION_IDS =
"non_autofillable_ime_action_ids";
+
+ /**
+ * Whether to enable autofill on all view types (not just checkbox, spinner, datepicker etc...)
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES =
+ "should_enable_autofill_on_all_view_types";
// END AUTOFILL FOR ALL APPS FLAGS //
@@ -315,6 +331,28 @@
}
/**
+ * Whether to apply heuristic check on important views before triggering fill request
+ *
+ * @hide
+ */
+ public static boolean isTriggerFillRequestOnFilteredImportantViewsEnabled() {
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_FILTERED_IMPORTANT_VIEWS, false);
+ }
+
+ /**
+ * Whether to enable autofill on all view types.
+ *
+ * @hide
+ */
+ public static boolean shouldEnableAutofillOnAllViewTypes(){
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES, false);
+ }
+
+ /**
* Get the non-autofillable ime actions from flag. This will be used in filtering
* condition to trigger fill request.
*
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 14c781b..0deaa76 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -674,6 +674,12 @@
// Indicate whether trigger fill request on unimportant views is enabled
private boolean mIsTriggerFillRequestOnUnimportantViewEnabled = false;
+ // Indicate whether to apply heuristic check on important views before trigger fill request
+ private boolean mIsTriggerFillRequestOnFilteredImportantViewsEnabled;
+
+ // Indicate whether to enable autofill for all view types
+ private boolean mShouldEnableAutofillOnAllViewTypes;
+
// A set containing all non-autofillable ime actions passed by flag
private Set<String> mNonAutofillableImeActionIdSet = new ArraySet<>();
@@ -855,6 +861,12 @@
mIsTriggerFillRequestOnUnimportantViewEnabled =
AutofillFeatureFlags.isTriggerFillRequestOnUnimportantViewEnabled();
+ mIsTriggerFillRequestOnFilteredImportantViewsEnabled =
+ AutofillFeatureFlags.isTriggerFillRequestOnFilteredImportantViewsEnabled();
+
+ mShouldEnableAutofillOnAllViewTypes =
+ AutofillFeatureFlags.shouldEnableAutofillOnAllViewTypes();
+
mNonAutofillableImeActionIdSet =
AutofillFeatureFlags.getNonAutofillableImeActionIdSetFromFlag();
@@ -865,14 +877,39 @@
mIsPackageFullyDeniedForAutofillForUnimportantView =
isPackageFullyDeniedForAutofillForUnimportantView(denyListString, packageName);
- mIsPackagePartiallyDeniedForAutofillForUnimportantView =
- isPackagePartiallyDeniedForAutofillForUnimportantView(denyListString, packageName);
+ if (!mIsPackageFullyDeniedForAutofillForUnimportantView) {
+ mIsPackagePartiallyDeniedForAutofillForUnimportantView =
+ isPackagePartiallyDeniedForAutofillForUnimportantView(denyListString, packageName);
+ }
if (mIsPackagePartiallyDeniedForAutofillForUnimportantView) {
setDeniedActivitySetWithDenyList(denyListString, packageName);
}
}
+ /**
+ * Whether to apply heuristic check on important views before triggering fill request
+ *
+ * @hide
+ */
+ public boolean isTriggerFillRequestOnFilteredImportantViewsEnabled() {
+ return mIsTriggerFillRequestOnFilteredImportantViewsEnabled;
+ }
+
+ /**
+ * Whether view passes the imeAction check
+ *
+ * @hide
+ */
+ public boolean isPassingImeActionCheck(EditText editText) {
+ final int actionId = editText.getImeOptions();
+ if (mNonAutofillableImeActionIdSet.contains(String.valueOf(actionId))) {
+ // TODO: add a log to indicate what has filtered out the view
+ return false;
+ }
+ return true;
+ }
+
private boolean isPackageFullyDeniedForAutofillForUnimportantView(
@NonNull String denyListString, @NonNull String packageName) {
// If "PackageName:;" is in the string, then it means the package name is in denylist
@@ -957,25 +994,32 @@
*
* @hide
*/
- public final boolean isMatchingAutofillableHeuristics(@NonNull View view) {
+ public final boolean isMatchingAutofillableHeuristicsForNotImportantViews(@NonNull View view) {
if (!mIsTriggerFillRequestOnUnimportantViewEnabled) {
return false;
}
+
+ // TODO: remove the autofill type check when this function is applied on both important and
+ // not important views.
+ // This check is needed here because once the view type check is lifted, addiditional
+ // unimportant views will be added to the assist structure which may cuase system health
+ // regression (viewGroup#populateChidlrenForAutofill() calls this function to decide whether
+ // to include child view)
+ if (view.getAutofillType() == View.AUTOFILL_TYPE_NONE) return false;
+
if (view instanceof EditText) {
- final int actionId = ((EditText) view).getImeOptions();
- if (mNonAutofillableImeActionIdSet.contains(String.valueOf(actionId))) {
- return false;
- }
- return true;
+ return isPassingImeActionCheck((EditText) view);
}
+
if (view instanceof CheckBox
- || view instanceof Spinner
- || view instanceof DatePicker
- || view instanceof TimePicker
- || view instanceof RadioGroup) {
+ || view instanceof Spinner
+ || view instanceof DatePicker
+ || view instanceof TimePicker
+ || view instanceof RadioGroup) {
return true;
}
- return false;
+
+ return mShouldEnableAutofillOnAllViewTypes;
}
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 78de954..0672d63 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -22,6 +22,8 @@
import android.os.Binder;
import android.os.BinderProxy;
import android.os.Debug;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -60,6 +62,7 @@
private static final int MAX_COUNT = 100;
private static final AtomicInteger sCounter = new AtomicInteger(0);
+ private static final int TRANSACTION_READY_TIMEOUT = 1000;
private static Supplier<Transaction> sTransactionFactory = Transaction::new;
@@ -111,6 +114,13 @@
*/
private final Binder mToken = new Binder();
+ private static final Object sHandlerThreadLock = new Object();
+ @GuardedBy("sHandlerThreadLock")
+ private static HandlerThread sHandlerThread;
+ private Handler mHandler;
+
+ private boolean mTimeoutAdded;
+
private static boolean isLocalBinder(IBinder binder) {
return !(binder instanceof BinderProxy);
}
@@ -538,6 +548,11 @@
+ transactionReadyCallback.hashCode());
}
+ // Start the timeout when this SurfaceSyncGroup has been added to a parent SurfaceSyncGroup.
+ // This is because if the other SurfaceSyncGroup has bugs and doesn't complete, this SSG
+ // will get stuck. It's better to complete this SSG even if the parent SSG is broken.
+ addTimeout();
+
boolean finished = false;
Runnable addedToSyncListener = null;
synchronized (mLock) {
@@ -641,6 +656,9 @@
}
mTransactionReadyConsumer.accept(mTransaction);
mFinished = true;
+ if (mTimeoutAdded) {
+ mHandler.removeCallbacksAndMessages(this);
+ }
}
/**
@@ -701,6 +719,12 @@
}
}
+ // Start the timeout when another SSG has been added to this SurfaceSyncGroup. This is
+ // because if the other SurfaceSyncGroup has bugs and doesn't complete, it will affect this
+ // SSGs. So it's better to just add a timeout in case the other SSG doesn't invoke the
+ // callback and complete this SSG.
+ addTimeout();
+
return transactionReadyCallback;
}
@@ -731,6 +755,41 @@
}
}
+ private void addTimeout() {
+ synchronized (sHandlerThreadLock) {
+ if (sHandlerThread == null) {
+ sHandlerThread = new HandlerThread("SurfaceSyncGroupTimer");
+ sHandlerThread.start();
+ }
+ }
+
+ synchronized (mLock) {
+ if (mTimeoutAdded) {
+ // We only need one timeout for the entire SurfaceSyncGroup since we just want to
+ // ensure it doesn't stay stuck forever.
+ return;
+ }
+
+ if (mHandler == null) {
+ mHandler = new Handler(sHandlerThread.getLooper());
+ }
+
+ mTimeoutAdded = true;
+ }
+
+ Runnable runnable = () -> {
+ Log.e(TAG, "Failed to receive transaction ready in " + TRANSACTION_READY_TIMEOUT
+ + "ms. Marking SurfaceSyncGroup as ready " + mName);
+ // Clear out any pending syncs in case the other syncs can't complete or timeout due to
+ // a crash.
+ synchronized (mLock) {
+ mPendingSyncs.clear();
+ }
+ markSyncReady();
+ };
+ mHandler.postDelayed(runnable, this, TRANSACTION_READY_TIMEOUT);
+ }
+
/**
* A frame callback that is used to synchronize SurfaceViews. The owner of the SurfaceView must
* implement onFrameStarted when trying to sync the SurfaceView. This is to ensure the sync
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index a646df3..a781454 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -18,7 +18,7 @@
import android.app.PendingIntent;
import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
import android.app.trust.IStrongAuthTracker;
import android.os.Bundle;
import android.security.keystore.recovery.WrappedApplicationKey;
@@ -95,7 +95,7 @@
in byte[] recoveryKeyBlob,
in List<WrappedApplicationKey> applicationKeys);
void closeSession(in String sessionId);
- StartLockscreenValidationRequest startRemoteLockscreenValidation();
+ RemoteLockscreenValidationSession startRemoteLockscreenValidation();
RemoteLockscreenValidationResult validateRemoteLockscreen(in byte[] encryptedCredential);
boolean hasSecureLockScreen();
boolean tryUnlockWithCachedUnifiedChallenge(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index b86020e..4d91410 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -29,7 +29,7 @@
import android.annotation.UserIdInt;
import android.app.PropertyInvalidatedCache;
import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
import android.app.admin.DevicePolicyManager;
import android.app.admin.PasswordMetrics;
import android.app.trust.IStrongAuthTracker;
@@ -1875,7 +1875,7 @@
* Starts a session to verify lockscreen credentials provided by a remote device.
*/
@NonNull
- public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+ public RemoteLockscreenValidationSession startRemoteLockscreenValidation() {
try {
return getLockSettings().startRemoteLockscreenValidation();
} catch (RemoteException e) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8fdefab..5be18fb4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4203,6 +4203,16 @@
<permission android:name="android.permission.WRITE_DEVICE_CONFIG"
android:protectionLevel="signature|verifier|configurator"/>
+ <!-- @SystemApi @TestApi @hide Allows an application to read/write sync disabled mode config.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG"
+ android:protectionLevel="signature|verifier|configurator"/>
+
+ <!-- @SystemApi @TestApi @hide Allows an application to modify only allowlisted settings.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG"
+ android:protectionLevel="signature|verifier|configurator"/>
+
<!-- @SystemApi @hide Allows an application to read config settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_DEVICE_CONFIG"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 16511a6..4d2747a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2772,7 +2772,8 @@
<!-- Whether the device allows users to start in background visible on the default display.
Should be false for most devices, except passenger-only automotive build (i.e., when
- Android runs in a separate system in the back seat to manage the passenger displays) -->
+ Android runs in a separate system in the back seat to manage the passenger displays).
+ When set to true, config_multiuserVisibleBackgroundUsers must also be true. -->
<bool name="config_multiuserVisibleBackgroundUsersOnDefaultDisplay">false</bool>
<!-- Whether to automatically switch to the designated Dock User (the user chosen for
@@ -3020,10 +3021,18 @@
>com.android.systemui/com.android.systemui.wifi.WifiDebuggingSecondaryUserActivity</string>
<!-- Package name of the system service that implements the shared connectivity service -->
- <string translatable="false" name="shared_connectivity_service_package"></string>
+ <string translatable="false" name="config_sharedConnectivityServicePackage"></string>
<!-- Intent action used when binding to the shared connectivity service -->
- <string translatable="false" name="shared_connectivity_service_intent_action"></string>
+ <string translatable="false" name="config_sharedConnectivityServiceIntentAction"></string>
+
+ <!-- The system and settings UI can support all the features of instant tether. If set to false,
+ instant tether will run in notifications mode -->
+ <bool name="config_hotspotNetworksEnabledForService">false</bool>
+
+ <!-- The system and settings UI can support all the features of known networks. If set to false,
+ known networks will run in notifications mode -->
+ <bool name="config_knownNetworksEnabledForService">false</bool>
<!-- Component name of the activity that shows the usb containment status. -->
<string name="config_usbContaminantActivity" translatable="false"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c34d31c..a15833d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4960,8 +4960,10 @@
<java-symbol type="bool" name="config_stopSystemPackagesByDefault"/>
<java-symbol type="string" name="config_wearServiceComponent" />
- <java-symbol type="string" name="shared_connectivity_service_package" />
- <java-symbol type="string" name="shared_connectivity_service_intent_action" />
+ <java-symbol type="string" name="config_sharedConnectivityServicePackage" />
+ <java-symbol type="string" name="config_sharedConnectivityServiceIntentAction" />
+ <java-symbol type="bool" name="config_hotspotNetworksEnabledForService"/>
+ <java-symbol type="bool" name="config_knownNetworksEnabledForService"/>
<!-- Whether to show weather on the lockscreen by default. -->
<java-symbol type="bool" name="config_lockscreenWeatherEnabledByDefault" />
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index ccf8085..b6fc137 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -91,7 +91,7 @@
mConfigsCacheGenerationStore = new MemoryIntArray(2);
mConfigsCacheGenerationStore.set(0, 123);
mConfigsCacheGenerationStore.set(1, 456);
- mSettingsCacheGenerationStore = new MemoryIntArray(3);
+ mSettingsCacheGenerationStore = new MemoryIntArray(2);
mSettingsCacheGenerationStore.set(0, 234);
mSettingsCacheGenerationStore.set(1, 567);
mConfigsStorage = new HashMap<>();
@@ -163,10 +163,6 @@
Bundle incomingBundle = invocationOnMock.getArgument(4);
String key = invocationOnMock.getArgument(3);
String value = incomingBundle.getString(Settings.NameValueTable.VALUE);
- boolean newSetting = false;
- if (!mSettingsStorage.containsKey(key)) {
- newSetting = true;
- }
mSettingsStorage.put(key, value);
int currentGeneration;
// Different settings have different generation codes
@@ -177,18 +173,12 @@
currentGeneration = mSettingsCacheGenerationStore.get(1);
mSettingsCacheGenerationStore.set(1, ++currentGeneration);
}
- if (newSetting) {
- // Tracking the generation of all unset settings.
- // Increment when a new setting is inserted
- currentGeneration = mSettingsCacheGenerationStore.get(2);
- mSettingsCacheGenerationStore.set(2, ++currentGeneration);
- }
return null;
});
// Returns the value corresponding to a setting, or null if the setting
- // doesn't have a value stored for it. Returns the generation key
- // if the caller asked for the generation key.
+ // doesn't have a value stored for it. Returns the generation key if the value isn't null
+ // and the caller asked for the generation key.
when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()),
eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class))).thenAnswer(
invocationOnMock -> {
@@ -199,15 +189,9 @@
Bundle bundle = new Bundle();
bundle.putSerializable(Settings.NameValueTable.VALUE, value);
- if (incomingBundle.containsKey(
+ if (value != null && incomingBundle.containsKey(
Settings.CALL_METHOD_TRACK_GENERATION_KEY)) {
- int index;
- if (value != null) {
- index = key.equals(SETTING) ? 0 : 1;
- } else {
- // special index for unset settings
- index = 2;
- }
+ int index = key.equals(SETTING) ? 0 : 1;
bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY,
mSettingsCacheGenerationStore);
bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
@@ -377,38 +361,16 @@
}
@Test
- public void testCaching_unsetSetting() throws Exception {
+ public void testCaching_nullSetting() throws Exception {
String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
verify(mMockIContentProvider, times(1)).call(any(), any(),
eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
assertThat(returnedValue).isNull();
String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
- // The first unset setting's generation number is cached
- verifyNoMoreInteractions(mMockIContentProvider);
- assertThat(cachedValue).isNull();
-
- String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2);
+ // Empty list won't be cached
verify(mMockIContentProvider, times(2)).call(any(), any(),
eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
- assertThat(returnedValue2).isNull();
-
- String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING);
- // The second unset setting's generation number is cached
- verifyNoMoreInteractions(mMockIContentProvider);
- assertThat(cachedValue2).isNull();
-
- Settings.Secure.putString(mMockContentResolver, SETTING, "a");
- // The generation for unset settings should have changed
- returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2);
- verify(mMockIContentProvider, times(3)).call(any(), any(),
- eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
- assertThat(returnedValue2).isNull();
-
- // The generation tracker for the first setting should have change because it's set now
- returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
- verify(mMockIContentProvider, times(4)).call(any(), any(),
- eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
- assertThat(returnedValue).isEqualTo("a");
+ assertThat(cachedValue).isNull();
}
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 913eaf2..ffc5ff2 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1099,6 +1099,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
+ "-1104347731": {
+ "message": "Setting requested orientation %s for %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-1103716954": {
"message": "Not removing %s due to exit animation",
"level": "VERBOSE",
diff --git a/data/keyboards/Vendor_004c_Product_0265.idc b/data/keyboards/Vendor_004c_Product_0265.idc
deleted file mode 120000
index 707dfcf..0000000
--- a/data/keyboards/Vendor_004c_Product_0265.idc
+++ /dev/null
@@ -1 +0,0 @@
-Vendor_05ac_Product_0265.idc
\ No newline at end of file
diff --git a/data/keyboards/Vendor_03f6_Product_a001.idc b/data/keyboards/Vendor_03f6_Product_a001.idc
deleted file mode 100644
index bcb4ee3..0000000
--- a/data/keyboards/Vendor_03f6_Product_a001.idc
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 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.
-
-#
-# Brydge Touchpad
-#
-
-# Reports from this touchpad sometimes get bunched together due to Bluetooth
-# batching, leading to bad timestamps that mess up finger velocity calculations.
-# To fix this, set a fake delta using the touchpad's known report rate.
-gestureProp.Fake_Timestamp_Delta = 0.010
diff --git a/data/keyboards/Vendor_046d_Product_4011.idc b/data/keyboards/Vendor_046d_Product_4011.idc
deleted file mode 100644
index 3a23830..0000000
--- a/data/keyboards/Vendor_046d_Product_4011.idc
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 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.
-
-#
-# Logitech Wireless Touchpad
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-gestureProp.Pressure_Calibration_Offset = -313.240741792594
-gestureProp.Pressure_Calibration_Slope = 4.39678062436752
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Palm_Pressure = 100000.0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/data/keyboards/Vendor_046d_Product_4101.idc b/data/keyboards/Vendor_046d_Product_4101.idc
deleted file mode 100644
index 47e2530..0000000
--- a/data/keyboards/Vendor_046d_Product_4101.idc
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 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.
-
-#
-# Logitech T650
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-gestureProp.Pressure_Calibration_Offset = -0.439288351750068
-gestureProp.Pressure_Calibration_Slope = 3.05998553523335
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/data/keyboards/Vendor_046d_Product_4102.idc b/data/keyboards/Vendor_046d_Product_4102.idc
deleted file mode 100644
index e33a28a..0000000
--- a/data/keyboards/Vendor_046d_Product_4102.idc
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 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.
-
-#
-# Logitech TK820
-#
-
-gestureProp.Touchpad_Stack_Version = 2
-# Pressure jumps around a lot on this touchpad, so allow that:
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Pressure_Calibration_Offset = -18.8078435
-gestureProp.Pressure_Calibration_Slope = 2.466208137
diff --git a/data/keyboards/Vendor_046d_Product_b00c.idc b/data/keyboards/Vendor_046d_Product_b00c.idc
deleted file mode 100644
index a49970c..0000000
--- a/data/keyboards/Vendor_046d_Product_b00c.idc
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 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.
-
-#
-# Logitech T651
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-gestureProp.Pressure_Calibration_Offset = -4.46520447177073
-gestureProp.Pressure_Calibration_Slope = 3.21071719332644
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/data/keyboards/Vendor_05ac_Product_0265.idc b/data/keyboards/Vendor_05ac_Product_0265.idc
deleted file mode 100644
index d72de64..0000000
--- a/data/keyboards/Vendor_05ac_Product_0265.idc
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 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.
-
-#
-# Apple Magic Trackpad 2 configuration file
-# Bluetooth vendor ID 004c
-# USB vendor ID 05ac
-#
-
-gestureProp.Pressure_Calibration_Offset = 30
-gestureProp.Palm_Pressure = 250.0
-gestureProp.Palm_Width = 20.0
-gestureProp.Multiple_Palm_Width = 20.0
-
-# Enable Stationary Wiggle Filter
-gestureProp.Stationary_Wiggle_Filter_Enabled = 1
-gestureProp.Finger_Moving_Energy = 0.0008
-gestureProp.Finger_Moving_Hysteresis = 0.0004
-
-# Avoid accidental scroll/move on finger lift
-gestureProp.Max_Stationary_Move_Speed = 47
-gestureProp.Max_Stationary_Move_Speed_Hysteresis = 1
-gestureProp.Max_Stationary_Move_Suppress_Distance = 0.2
diff --git a/data/keyboards/Vendor_05ac_Product_030e.idc b/data/keyboards/Vendor_05ac_Product_030e.idc
deleted file mode 100644
index 23a2e18..0000000
--- a/data/keyboards/Vendor_05ac_Product_030e.idc
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 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.
-
-#
-# Apple Magic Trackpad
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-# We are using raw touch major value as pressure value, so set the Palm
-# pressure threshold high.
-gestureProp.Palm_Pressure = 1000
-gestureProp.Compute_Surface_Area_from_Pressure = 0
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-# NOTE: bias on X-axis is uncalibrated
-gestureProp.Touchpad_Device_Output_Bias_on_X-Axis = -283.3226025266607
-gestureProp.Touchpad_Device_Output_Bias_on_Y-Axis = -283.3226025266607
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-# Drumroll suppression causes janky movement on this touchpad.
-gestureProp.Drumroll_Suppression_Enable = 0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
index 68ff806..65955b1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Rect;
+import android.hardware.devicestate.DeviceStateManager;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -33,7 +34,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-/** A representation of a folding feature for both Extension and Sidecar.
+/**
+ * A representation of a folding feature for both Extension and Sidecar.
* For Sidecar this is the same as combining {@link androidx.window.sidecar.SidecarDeviceState} and
* {@link androidx.window.sidecar.SidecarDisplayFeature}. For Extensions this is the mirror of
* {@link androidx.window.extensions.layout.FoldingFeature}.
@@ -67,10 +69,11 @@
public static final int COMMON_STATE_UNKNOWN = -1;
/**
- * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
- * and Extensions do not match exactly.
+ * A common state that contains no folding features. For example, an in-folding device in the
+ * "closed" device state.
*/
- public static final int COMMON_STATE_FLAT = 3;
+ public static final int COMMON_STATE_NO_FOLDING_FEATURES = 1;
+
/**
* A common state to represent a HALF_OPENED hinge. This is needed because the definitions in
* Sidecar and Extensions do not match exactly.
@@ -78,9 +81,27 @@
public static final int COMMON_STATE_HALF_OPENED = 2;
/**
- * The possible states for a folding hinge.
+ * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
+ * and Extensions do not match exactly.
*/
- @IntDef({COMMON_STATE_UNKNOWN, COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED})
+ public static final int COMMON_STATE_FLAT = 3;
+
+ /**
+ * A common state where the hinge state should be derived using the base state from
+ * {@link DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int)} instead of the
+ * emulated state. This is an internal state and must not be passed to clients.
+ */
+ public static final int COMMON_STATE_USE_BASE_STATE = 1000;
+
+ /**
+ * The possible states for a folding hinge. Common in this context means normalized between
+ * extensions and sidecar.
+ */
+ @IntDef({COMMON_STATE_UNKNOWN,
+ COMMON_STATE_NO_FOLDING_FEATURES,
+ COMMON_STATE_HALF_OPENED,
+ COMMON_STATE_FLAT,
+ COMMON_STATE_USE_BASE_STATE})
@Retention(RetentionPolicy.SOURCE)
public @interface State {
}
@@ -167,7 +188,7 @@
}
String stateString = featureMatcher.group(6);
stateString = stateString == null ? "" : stateString;
- final int state;
+ @State final int state;
switch (stateString) {
case PATTERN_STATE_FLAT:
state = COMMON_STATE_FLAT;
@@ -191,8 +212,8 @@
@NonNull
private final Rect mRect;
- CommonFoldingFeature(int type, int state, @NonNull Rect rect) {
- assertValidState(state);
+ CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) {
+ assertReportableState(state);
this.mType = type;
this.mState = state;
if (rect.width() == 0 && rect.height() == 0) {
@@ -231,13 +252,22 @@
}
@Override
+ public String toString() {
+ return "CommonFoldingFeature=[Type: " + mType + ", state: " + mState + "]";
+ }
+
+ @Override
public int hashCode() {
return Objects.hash(mType, mState, mRect);
}
- private static void assertValidState(@Nullable Integer state) {
- if (state != null && state != COMMON_STATE_FLAT
- && state != COMMON_STATE_HALF_OPENED && state != COMMON_STATE_UNKNOWN) {
+ /**
+ * Checks if the provided folding feature state should be reported to clients. See
+ * {@link androidx.window.extensions.layout.FoldingFeature}
+ */
+ private static void assertReportableState(@State int state) {
+ if (state != COMMON_STATE_FLAT && state != COMMON_STATE_HALF_OPENED
+ && state != COMMON_STATE_UNKNOWN) {
throw new IllegalArgumentException("Invalid state: " + state
+ "must be either COMMON_STATE_FLAT or COMMON_STATE_HALF_OPENED");
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index 0bdf98c..66f27f5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -19,6 +19,7 @@
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE;
import static androidx.window.common.CommonFoldingFeature.parseListFromString;
import android.annotation.NonNull;
@@ -52,13 +53,54 @@
DeviceStateManagerFoldingFeatureProducer.class.getSimpleName();
private static final boolean DEBUG = false;
+ /**
+ * Emulated device state {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} to
+ * {@link CommonFoldingFeature.State} map.
+ */
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
+ /**
+ * Emulated device state received via
+ * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
+ * "Emulated" states differ from "base" state in the sense that they may not correspond 1:1 with
+ * physical device states. They represent the state of the device when various software
+ * features and APIs are applied. The emulated states generally consist of all "base" states,
+ * but may have additional states such as "concurrent" or "rear display". Concurrent mode for
+ * example is activated via public API and can be active in both the "open" and "half folded"
+ * device states.
+ */
private int mCurrentDeviceState = INVALID_DEVICE_STATE;
+ /**
+ * Base device state received via
+ * {@link DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int)}.
+ * "Base" in this context means the "physical" state of the device.
+ */
+ private int mCurrentBaseDeviceState = INVALID_DEVICE_STATE;
+
@NonNull
private final BaseDataProducer<String> mRawFoldSupplier;
+ private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() {
+ @Override
+ public void onStateChanged(int state) {
+ mCurrentDeviceState = state;
+ mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
+ .this::notifyFoldingFeatureChange);
+ }
+
+ @Override
+ public void onBaseStateChanged(int state) {
+ mCurrentBaseDeviceState = state;
+
+ if (mDeviceStateToPostureMap.get(mCurrentDeviceState)
+ == COMMON_STATE_USE_BASE_STATE) {
+ mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
+ .this::notifyFoldingFeatureChange);
+ }
+ }
+ };
+
public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context,
@NonNull BaseDataProducer<String> rawFoldSupplier) {
mRawFoldSupplier = rawFoldSupplier;
@@ -92,12 +134,8 @@
}
if (mDeviceStateToPostureMap.size() > 0) {
- DeviceStateCallback deviceStateCallback = (state) -> {
- mCurrentDeviceState = state;
- mRawFoldSupplier.getData(this::notifyFoldingFeatureChange);
- };
Objects.requireNonNull(context.getSystemService(DeviceStateManager.class))
- .registerCallback(context.getMainExecutor(), deviceStateCallback);
+ .registerCallback(context.getMainExecutor(), mDeviceStateCallback);
}
}
@@ -178,11 +216,18 @@
}
private List<CommonFoldingFeature> calculateFoldingFeature(String displayFeaturesString) {
- final int globalHingeState = globalHingeState();
- return parseListFromString(displayFeaturesString, globalHingeState);
+ return parseListFromString(displayFeaturesString, currentHingeState());
}
- private int globalHingeState() {
- return mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
+ @CommonFoldingFeature.State
+ private int currentHingeState() {
+ @CommonFoldingFeature.State
+ int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
+
+ if (posture == CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE) {
+ posture = mDeviceStateToPostureMap.get(mCurrentBaseDeviceState, COMMON_STATE_UNKNOWN);
+ }
+
+ return posture;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 852fae6..ef53839 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -139,6 +139,30 @@
private static final boolean BUBBLE_BAR_ENABLED =
SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
+
+ /**
+ * Common interface to send updates to bubble views.
+ */
+ public interface BubbleViewCallback {
+ /** Called when the provided bubble should be removed. */
+ void removeBubble(Bubble removedBubble);
+ /** Called when the provided bubble should be added. */
+ void addBubble(Bubble addedBubble);
+ /** Called when the provided bubble should be updated. */
+ void updateBubble(Bubble updatedBubble);
+ /** Called when the provided bubble should be selected. */
+ void selectionChanged(BubbleViewProvider selectedBubble);
+ /** Called when the provided bubble's suppression state has changed. */
+ void suppressionChanged(Bubble bubble, boolean isSuppressed);
+ /** Called when the expansion state of bubbles has changed. */
+ void expansionChanged(boolean isExpanded);
+ /**
+ * Called when the order of the bubble list has changed. Depending on the expanded state
+ * the pointer might need to be updated.
+ */
+ void bubbleOrderChanged(List<Bubble> bubbleOrder, boolean updatePointer);
+ }
+
private final Context mContext;
private final BubblesImpl mImpl = new BubblesImpl();
private Bubbles.BubbleExpandListener mExpandListener;
@@ -162,7 +186,6 @@
// Used to post to main UI thread
private final ShellExecutor mMainExecutor;
private final Handler mMainHandler;
-
private final ShellExecutor mBackgroundExecutor;
private BubbleLogger mLogger;
@@ -1320,6 +1343,58 @@
});
}
+ private final BubbleViewCallback mBubbleViewCallback = new BubbleViewCallback() {
+ @Override
+ public void removeBubble(Bubble removedBubble) {
+ if (mStackView != null) {
+ mStackView.removeBubble(removedBubble);
+ }
+ }
+
+ @Override
+ public void addBubble(Bubble addedBubble) {
+ if (mStackView != null) {
+ mStackView.addBubble(addedBubble);
+ }
+ }
+
+ @Override
+ public void updateBubble(Bubble updatedBubble) {
+ if (mStackView != null) {
+ mStackView.updateBubble(updatedBubble);
+ }
+ }
+
+ @Override
+ public void bubbleOrderChanged(List<Bubble> bubbleOrder, boolean updatePointer) {
+ if (mStackView != null) {
+ mStackView.updateBubbleOrder(bubbleOrder, updatePointer);
+ }
+ }
+
+ @Override
+ public void suppressionChanged(Bubble bubble, boolean isSuppressed) {
+ if (mStackView != null) {
+ mStackView.setBubbleSuppressed(bubble, isSuppressed);
+ }
+ }
+
+ @Override
+ public void expansionChanged(boolean isExpanded) {
+ if (mStackView != null) {
+ mStackView.setExpanded(isExpanded);
+ }
+ }
+
+ @Override
+ public void selectionChanged(BubbleViewProvider selectedBubble) {
+ if (mStackView != null) {
+ mStackView.setSelectedBubble(selectedBubble);
+ }
+
+ }
+ };
+
@SuppressWarnings("FieldCanBeLocal")
private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
@@ -1342,7 +1417,8 @@
// Lazy load overflow bubbles from disk
loadOverflowBubblesFromDisk();
- mStackView.updateOverflowButtonDot();
+ // If bubbles in the overflow have a dot, make sure the overflow shows a dot
+ updateOverflowButtonDot();
// Update bubbles in overflow.
if (mOverflowListener != null) {
@@ -1357,9 +1433,7 @@
final Bubble bubble = removed.first;
@Bubbles.DismissReason final int reason = removed.second;
- if (mStackView != null) {
- mStackView.removeBubble(bubble);
- }
+ mBubbleViewCallback.removeBubble(bubble);
// Leave the notification in place if we're dismissing due to user switching, or
// because DND is suppressing the bubble. In both of those cases, we need to be able
@@ -1389,49 +1463,47 @@
}
mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
- if (update.addedBubble != null && mStackView != null) {
+ if (update.addedBubble != null) {
mDataRepository.addBubble(mCurrentUserId, update.addedBubble);
- mStackView.addBubble(update.addedBubble);
+ mBubbleViewCallback.addBubble(update.addedBubble);
}
- if (update.updatedBubble != null && mStackView != null) {
- mStackView.updateBubble(update.updatedBubble);
+ if (update.updatedBubble != null) {
+ mBubbleViewCallback.updateBubble(update.updatedBubble);
}
- if (update.suppressedBubble != null && mStackView != null) {
- mStackView.setBubbleSuppressed(update.suppressedBubble, true);
+ if (update.suppressedBubble != null) {
+ mBubbleViewCallback.suppressionChanged(update.suppressedBubble, true);
}
- if (update.unsuppressedBubble != null && mStackView != null) {
- mStackView.setBubbleSuppressed(update.unsuppressedBubble, false);
+ if (update.unsuppressedBubble != null) {
+ mBubbleViewCallback.suppressionChanged(update.unsuppressedBubble, false);
}
boolean collapseStack = update.expandedChanged && !update.expanded;
// At this point, the correct bubbles are inflated in the stack.
// Make sure the order in bubble data is reflected in bubble row.
- if (update.orderChanged && mStackView != null) {
+ if (update.orderChanged) {
mDataRepository.addBubbles(mCurrentUserId, update.bubbles);
// if the stack is going to be collapsed, do not update pointer position
// after reordering
- mStackView.updateBubbleOrder(update.bubbles, !collapseStack);
+ mBubbleViewCallback.bubbleOrderChanged(update.bubbles, !collapseStack);
}
if (collapseStack) {
- mStackView.setExpanded(false);
+ mBubbleViewCallback.expansionChanged(/* expanded= */ false);
mSysuiProxy.requestNotificationShadeTopUi(false, TAG);
}
- if (update.selectionChanged && mStackView != null) {
- mStackView.setSelectedBubble(update.selectedBubble);
+ if (update.selectionChanged) {
+ mBubbleViewCallback.selectionChanged(update.selectedBubble);
}
// Expanding? Apply this last.
if (update.expandedChanged && update.expanded) {
- if (mStackView != null) {
- mStackView.setExpanded(true);
- mSysuiProxy.requestNotificationShadeTopUi(true, TAG);
- }
+ mBubbleViewCallback.expansionChanged(/* expanded= */ true);
+ mSysuiProxy.requestNotificationShadeTopUi(true, TAG);
}
mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate");
@@ -1442,6 +1514,19 @@
}
};
+ private void updateOverflowButtonDot() {
+ BubbleOverflow overflow = mBubbleData.getOverflow();
+ if (overflow == null) return;
+
+ for (Bubble b : mBubbleData.getOverflowBubbles()) {
+ if (b.showDot()) {
+ overflow.setShowDot(true);
+ return;
+ }
+ }
+ overflow.setShowDot(false);
+ }
+
private boolean handleDismissalInterception(BubbleEntry entry,
@Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
if (isSummaryOfBubbles(entry)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 7d71089e..0b947c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1359,16 +1359,6 @@
updateOverflowVisibility();
}
- void updateOverflowButtonDot() {
- for (Bubble b : mBubbleData.getOverflowBubbles()) {
- if (b.showDot()) {
- mBubbleOverflow.setShowDot(true);
- return;
- }
- }
- mBubbleOverflow.setShowDot(false);
- }
-
/**
* Handle theme changes.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index ab96856..3d1ed87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -31,6 +31,7 @@
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipAppOpsListener;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -70,7 +71,7 @@
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
- PipSizeSpecHandler pipSizeSpecHandler,
+ PipDisplayLayoutState pipDisplayLayoutState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
PipAppOpsListener pipAppOpsListener,
@@ -91,7 +92,7 @@
shellInit,
shellController,
tvPipBoundsState,
- pipSizeSpecHandler,
+ pipDisplayLayoutState,
tvPipBoundsAlgorithm,
tvPipBoundsController,
pipAppOpsListener,
@@ -141,14 +142,15 @@
@WMSingleton
@Provides
static TvPipBoundsState provideTvPipBoundsState(Context context,
- PipSizeSpecHandler pipSizeSpecHandler) {
- return new TvPipBoundsState(context, pipSizeSpecHandler);
+ PipSizeSpecHandler pipSizeSpecHandler, PipDisplayLayoutState pipDisplayLayoutState) {
+ return new TvPipBoundsState(context, pipSizeSpecHandler, pipDisplayLayoutState);
}
@WMSingleton
@Provides
- static PipSizeSpecHandler providePipSizeSpecHelper(Context context) {
- return new PipSizeSpecHandler(context);
+ static PipSizeSpecHandler providePipSizeSpecHelper(Context context,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ return new PipSizeSpecHandler(context, pipDisplayLayoutState);
}
// Handler needed for loadDrawableAsync() in PipControlsViewController
@@ -203,7 +205,7 @@
TvPipMenuController tvPipMenuController,
SyncTransactionQueue syncTransactionQueue,
TvPipBoundsState tvPipBoundsState,
- PipSizeSpecHandler pipSizeSpecHandler,
+ PipDisplayLayoutState pipDisplayLayoutState,
PipTransitionState pipTransitionState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipAnimationController pipAnimationController,
@@ -215,7 +217,7 @@
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new TvPipTaskOrganizer(context,
- syncTransactionQueue, pipTransitionState, tvPipBoundsState, pipSizeSpecHandler,
+ syncTransactionQueue, pipTransitionState, tvPipBoundsState, pipDisplayLayoutState,
tvPipBoundsAlgorithm, tvPipMenuController, pipAnimationController,
pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
splitScreenControllerOptional, displayController, pipUiEventLogger,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 948bf2d..ba0f073 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -65,6 +65,7 @@
import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -344,6 +345,7 @@
PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
PipBoundsState pipBoundsState,
PipSizeSpecHandler pipSizeSpecHandler,
+ PipDisplayLayoutState pipDisplayLayoutState,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
@@ -360,18 +362,18 @@
return Optional.ofNullable(PipController.create(
context, shellInit, shellCommandHandler, shellController,
displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm,
- pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler, pipMotionHelper,
- pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
- pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
- taskStackListener, pipParamsChangedForwarder, displayInsetsController,
- oneHandedController, mainExecutor));
+ pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler, pipDisplayLayoutState,
+ pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
+ pipTransitionState, pipTouchHandler, pipTransitionController,
+ windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
+ displayInsetsController, oneHandedController, mainExecutor));
}
@WMSingleton
@Provides
static PipBoundsState providePipBoundsState(Context context,
- PipSizeSpecHandler pipSizeSpecHandler) {
- return new PipBoundsState(context, pipSizeSpecHandler);
+ PipSizeSpecHandler pipSizeSpecHandler, PipDisplayLayoutState pipDisplayLayoutState) {
+ return new PipBoundsState(context, pipSizeSpecHandler, pipDisplayLayoutState);
}
@WMSingleton
@@ -388,8 +390,9 @@
@WMSingleton
@Provides
- static PipSizeSpecHandler providePipSizeSpecHelper(Context context) {
- return new PipSizeSpecHandler(context);
+ static PipSizeSpecHandler providePipSizeSpecHelper(Context context,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ return new PipSizeSpecHandler(context, pipDisplayLayoutState);
}
@WMSingleton
@@ -446,7 +449,7 @@
SyncTransactionQueue syncTransactionQueue,
PipTransitionState pipTransitionState,
PipBoundsState pipBoundsState,
- PipSizeSpecHandler pipSizeSpecHandler,
+ PipDisplayLayoutState pipDisplayLayoutState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PhonePipMenuController menuPhoneController,
PipAnimationController pipAnimationController,
@@ -458,7 +461,7 @@
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
- syncTransactionQueue, pipTransitionState, pipBoundsState, pipSizeSpecHandler,
+ syncTransactionQueue, pipTransitionState, pipBoundsState, pipDisplayLayoutState,
pipBoundsAlgorithm, menuPhoneController, pipAnimationController,
pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
splitScreenControllerOptional, displayController, pipUiEventLogger,
@@ -477,12 +480,12 @@
static PipTransitionController providePipTransitionController(Context context,
ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PipSizeSpecHandler pipSizeSpecHandler,
+ PipBoundsState pipBoundsState, PipDisplayLayoutState pipDisplayLayoutState,
PipTransitionState pipTransitionState, PhonePipMenuController pipMenuController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenOptional) {
return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
- pipBoundsState, pipSizeSpecHandler, pipTransitionState, pipMenuController,
+ pipBoundsState, pipDisplayLayoutState, pipTransitionState, pipMenuController,
pipBoundsAlgorithm, pipAnimationController, pipSurfaceTransactionHelper,
splitScreenOptional);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index 9796e4c..2d84d21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -267,6 +267,11 @@
mLaunchRootTask = taskInfo;
}
+ if (mHomeTask != null && mHomeTask.taskId == taskInfo.taskId
+ && !taskInfo.equals(mHomeTask)) {
+ mHomeTask = taskInfo;
+ }
+
super.onTaskInfoChanged(taskInfo);
}
@@ -376,6 +381,7 @@
final WindowContainerTransaction wct = getWindowContainerTransaction();
final Rect taskBounds = calculateBounds();
wct.setBounds(mLaunchRootTask.token, taskBounds);
+ wct.setBounds(mHomeTask.token, new Rect(0, 0, mDisplayWidth, mDisplayHeight));
mSyncQueue.queue(wct);
final SurfaceControl finalLeash = mLaunchRootLeash;
mSyncQueue.runInSync(t -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
index 2624ee5..d961d86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
@@ -70,4 +70,9 @@
* Sets the next pip animation type to be the alpha animation.
*/
oneway void setPipAnimationTypeToAlpha() = 5;
+
+ /**
+ * Sets the height and visibility of the Launcher keep clear area.
+ */
+ oneway void setLauncherKeepClearAreaHeight(boolean visible, int height) = 6;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 5be18d8..f664808 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -30,7 +30,6 @@
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Size;
-import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -77,6 +76,7 @@
private final @NonNull Rect mExpandedBounds = new Rect();
private final @NonNull Rect mNormalMovementBounds = new Rect();
private final @NonNull Rect mExpandedMovementBounds = new Rect();
+ private final @NonNull PipDisplayLayoutState mPipDisplayLayoutState;
private final Point mMaxSize = new Point();
private final Point mMinSize = new Point();
private final @NonNull Context mContext;
@@ -86,8 +86,6 @@
private @Nullable PipReentryState mPipReentryState;
private final @Nullable PipSizeSpecHandler mPipSizeSpecHandler;
private @Nullable ComponentName mLastPipComponentName;
- private int mDisplayId = Display.DEFAULT_DISPLAY;
- private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout();
private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
private boolean mIsImeShowing;
private int mImeHeight;
@@ -120,10 +118,12 @@
private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
- public PipBoundsState(@NonNull Context context, PipSizeSpecHandler pipSizeSpecHandler) {
+ public PipBoundsState(@NonNull Context context, PipSizeSpecHandler pipSizeSpecHandler,
+ PipDisplayLayoutState pipDisplayLayoutState) {
mContext = context;
reloadResources();
mPipSizeSpecHandler = pipSizeSpecHandler;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
}
/** Reloads the resources. */
@@ -290,31 +290,16 @@
return mLastPipComponentName;
}
- /** Get the current display id. */
- public int getDisplayId() {
- return mDisplayId;
- }
-
- /** Set the current display id for the associated display layout. */
- public void setDisplayId(int displayId) {
- mDisplayId = displayId;
- }
-
/** Returns the display's bounds. */
@NonNull
public Rect getDisplayBounds() {
- return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
- }
-
- /** Update the display layout. */
- public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
- mDisplayLayout.set(displayLayout);
+ return mPipDisplayLayoutState.getDisplayBounds();
}
/** Get a copy of the display layout. */
@NonNull
public DisplayLayout getDisplayLayout() {
- return new DisplayLayout(mDisplayLayout);
+ return mPipDisplayLayoutState.getDisplayLayout();
}
@VisibleForTesting
@@ -568,7 +553,6 @@
pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
- pw.println(innerPrefix + "mDisplayId=" + mDisplayId);
pw.println(innerPrefix + "mStashedState=" + mStashedState);
pw.println(innerPrefix + "mStashOffset=" + mStashOffset);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
new file mode 100644
index 0000000..0f76af4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.dagger.WMSingleton;
+
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+
+/**
+ * Acts as a source of truth for display related information for PIP.
+ */
+@WMSingleton
+public class PipDisplayLayoutState {
+ private static final String TAG = PipDisplayLayoutState.class.getSimpleName();
+
+ private Context mContext;
+ private int mDisplayId;
+ @NonNull private DisplayLayout mDisplayLayout;
+
+ @Inject
+ public PipDisplayLayoutState(Context context) {
+ mContext = context;
+ mDisplayLayout = new DisplayLayout();
+ }
+
+ /** Update the display layout. */
+ public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
+ mDisplayLayout.set(displayLayout);
+ }
+
+ /** Get a copy of the display layout. */
+ @NonNull
+ public DisplayLayout getDisplayLayout() {
+ return new DisplayLayout(mDisplayLayout);
+ }
+
+ /** Get the display bounds */
+ @NonNull
+ public Rect getDisplayBounds() {
+ return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+ }
+
+ /**
+ * Apply a rotation to this layout and its parameters.
+ * @param targetRotation
+ */
+ public void rotateTo(@Surface.Rotation int targetRotation) {
+ mDisplayLayout.rotateTo(mContext.getResources(), targetRotation);
+ }
+
+ /** Get the current display id */
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ /** Set the current display id for the associated display layout. */
+ public void setDisplayId(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ /** Dumps internal state. */
+ public void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + "mDisplayId=" + mDisplayId);
+ pw.println(innerPrefix + "getDisplayBounds=" + getDisplayBounds());
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index e9d2571..d5b9c5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -79,13 +79,11 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.pip.phone.PipMotionHelper;
-import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
@@ -128,7 +126,7 @@
private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState;
- private final PipSizeSpecHandler mPipSizeSpecHandler;
+ private final PipDisplayLayoutState mPipDisplayLayoutState;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final @NonNull PipMenuController mPipMenuController;
private final PipAnimationController mPipAnimationController;
@@ -316,7 +314,7 @@
@NonNull SyncTransactionQueue syncTransactionQueue,
@NonNull PipTransitionState pipTransitionState,
@NonNull PipBoundsState pipBoundsState,
- @NonNull PipSizeSpecHandler pipSizeSpecHandler,
+ @NonNull PipDisplayLayoutState pipDisplayLayoutState,
@NonNull PipBoundsAlgorithm boundsHandler,
@NonNull PipMenuController pipMenuController,
@NonNull PipAnimationController pipAnimationController,
@@ -332,7 +330,7 @@
mSyncTransactionQueue = syncTransactionQueue;
mPipTransitionState = pipTransitionState;
mPipBoundsState = pipBoundsState;
- mPipSizeSpecHandler = pipSizeSpecHandler;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
mPipBoundsAlgorithm = boundsHandler;
mPipMenuController = pipMenuController;
mPipTransitionController = pipTransitionController;
@@ -653,7 +651,7 @@
// If the displayId of the task is different than what PipBoundsHandler has, then update
// it. This is possible if we entered PiP on an external display.
- if (info.displayId != mPipBoundsState.getDisplayId()
+ if (info.displayId != mPipDisplayLayoutState.getDisplayId()
&& mOnDisplayIdChangeCallback != null) {
mOnDisplayIdChangeCallback.accept(info.displayId);
}
@@ -1179,6 +1177,20 @@
}
/**
+ * Directly update the animator bounds.
+ */
+ public void updateAnimatorBounds(Rect bounds) {
+ final PipAnimationController.PipTransitionAnimator animator =
+ mPipAnimationController.getCurrentAnimator();
+ if (animator != null && animator.isRunning()) {
+ if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
+ animator.updateEndValue(bounds);
+ }
+ animator.setDestinationBounds(bounds);
+ }
+ }
+
+ /**
* Handles all changes to the PictureInPictureParams.
*/
protected void applyNewPictureInPictureParams(@NonNull PictureInPictureParams params) {
@@ -1621,15 +1633,15 @@
return animator;
}
- /** Computes destination bounds in old rotation and returns source hint rect if available. */
+ /** Computes destination bounds in old rotation and returns source hint rect if available.
+ *
+ * Note: updates the internal state of {@link PipDisplayLayoutState} by applying a rotation
+ * transformation onto the display layout.
+ */
private @Nullable Rect computeRotatedBounds(int rotationDelta, int direction,
Rect outDestinationBounds, Rect sourceHintRect) {
if (direction == TRANSITION_DIRECTION_TO_PIP) {
- DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
-
- layoutCopy.rotateTo(mContext.getResources(), mNextRotation);
- mPipBoundsState.setDisplayLayout(layoutCopy);
- mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+ mPipDisplayLayoutState.rotateTo(mNextRotation);
final Rect displayBounds = mPipBoundsState.getDisplayBounds();
outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index a91a342..2848f34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -64,8 +64,6 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
@@ -85,7 +83,7 @@
private final Context mContext;
private final PipTransitionState mPipTransitionState;
- private final PipSizeSpecHandler mPipSizeSpecHandler;
+ private final PipDisplayLayoutState mPipDisplayLayoutState;
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<SplitScreenController> mSplitScreenOptional;
@@ -116,7 +114,7 @@
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
- PipSizeSpecHandler pipSizeSpecHandler,
+ PipDisplayLayoutState pipDisplayLayoutState,
PipTransitionState pipTransitionState,
PipMenuController pipMenuController,
PipBoundsAlgorithm pipBoundsAlgorithm,
@@ -127,7 +125,7 @@
pipBoundsAlgorithm, pipAnimationController);
mContext = context;
mPipTransitionState = pipTransitionState;
- mPipSizeSpecHandler = pipSizeSpecHandler;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
@@ -313,11 +311,7 @@
// initial state under the new rotation.
int rotationDelta = deltaRotation(startRotation, endRotation);
if (rotationDelta != Surface.ROTATION_0) {
- DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
-
- layoutCopy.rotateTo(mContext.getResources(), endRotation);
- mPipBoundsState.setDisplayLayout(layoutCopy);
- mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+ mPipDisplayLayoutState.rotateTo(endRotation);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
wct.setBounds(mRequestedEnterTask, destinationBounds);
@@ -398,7 +392,7 @@
// Launcher may update the Shelf height during the animation, which will update the
// destination bounds. Because this is in fixed rotation, We need to make sure the
// finishTransaction is using the updated bounds in the display rotation.
- final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+ final Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds();
final Rect finishBounds = new Rect(destinationBounds);
rotateBounds(finishBounds, displayBounds, mEndFixedRotation, displayRotation);
mSurfaceTransactionHelper.crop(mFinishTransaction, leash, finishBounds);
@@ -640,7 +634,7 @@
@NonNull TaskInfo taskInfo) {
startTransaction.apply();
finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
- mPipBoundsState.getDisplayBounds());
+ mPipDisplayLayoutState.getDisplayBounds());
mPipOrganizer.onExitPipFinished(taskInfo);
finishCallback.onTransitionFinished(null, null);
}
@@ -834,13 +828,9 @@
/** Computes destination bounds in old rotation and updates source hint rect if available. */
private void computeEnterPipRotatedBounds(int rotationDelta, int startRotation, int endRotation,
TaskInfo taskInfo, Rect outDestinationBounds, @Nullable Rect outSourceHintRect) {
- DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
+ mPipDisplayLayoutState.rotateTo(endRotation);
- layoutCopy.rotateTo(mContext.getResources(), endRotation);
- mPipBoundsState.setDisplayLayout(layoutCopy);
- mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
-
- final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+ final Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds();
outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
// Transform the destination bounds to current display coordinates.
rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
index c6b5ce9..db6138a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
@@ -93,6 +93,11 @@
return hasEnteredPip(mState);
}
+ /** Returns true if activity is currently entering PiP mode. */
+ public boolean isEnteringPip() {
+ return isEnteringPip(mState);
+ }
+
public void setInSwipePipToHomeTransition(boolean inSwipePipToHomeTransition) {
mInSwipePipToHomeTransition = inSwipePipToHomeTransition;
}
@@ -130,6 +135,11 @@
return state == ENTERED_PIP;
}
+ /** Returns true if activity is currently entering PiP mode. */
+ public static boolean isEnteringPip(@TransitionState int state) {
+ return state == ENTERING_PIP;
+ }
+
public interface OnPipTransitionStateChangedListener {
void onPipTransitionStateChanged(@TransitionState int oldState,
@TransitionState int newState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index a1483a8..db6ef1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -83,6 +83,7 @@
import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
@@ -101,6 +102,7 @@
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -138,6 +140,7 @@
private PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm;
private PipBoundsState mPipBoundsState;
private PipSizeSpecHandler mPipSizeSpecHandler;
+ private PipDisplayLayoutState mPipDisplayLayoutState;
private PipMotionHelper mPipMotionHelper;
private PipTouchHandler mTouchHandler;
private PipTransitionController mPipTransitionController;
@@ -181,14 +184,20 @@
// early bail out if the keep clear areas feature is disabled
return;
}
- // only move if already in pip, other transitions account for keep clear areas
- if (mPipTransitionState.hasEnteredPip()) {
+ // only move if we're in PiP or transitioning into PiP
+ if (!mPipTransitionState.shouldBlockResizeRequest()) {
Rect destBounds = mPipKeepClearAlgorithm.adjust(mPipBoundsState,
mPipBoundsAlgorithm);
// only move if the bounds are actually different
if (destBounds != mPipBoundsState.getBounds()) {
- mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
- mEnterAnimationDuration, null);
+ if (mPipTransitionState.hasEnteredPip()) {
+ // if already in PiP, schedule separate animation
+ mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
+ mEnterAnimationDuration, null);
+ } else if (mPipTransitionState.isEnteringPip()) {
+ // while entering PiP we just need to update animator bounds
+ mPipTaskOrganizer.updateAnimatorBounds(destBounds);
+ }
}
}
}
@@ -303,7 +312,7 @@
@Override
public void onDisplayAdded(int displayId) {
- if (displayId != mPipBoundsState.getDisplayId()) {
+ if (displayId != mPipDisplayLayoutState.getDisplayId()) {
return;
}
onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
@@ -312,7 +321,7 @@
@Override
public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
- if (displayId != mPipBoundsState.getDisplayId()) {
+ if (displayId != mPipDisplayLayoutState.getDisplayId()) {
return;
}
onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
@@ -322,7 +331,7 @@
@Override
public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
Set<Rect> unrestricted) {
- if (mPipBoundsState.getDisplayId() == displayId) {
+ if (mPipDisplayLayoutState.getDisplayId() == displayId) {
if (mEnablePipKeepClearAlgorithm) {
mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
@@ -382,6 +391,7 @@
PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
PipBoundsState pipBoundsState,
PipSizeSpecHandler pipSizeSpecHandler,
+ PipDisplayLayoutState pipDisplayLayoutState,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
@@ -404,8 +414,8 @@
return new PipController(context, shellInit, shellCommandHandler, shellController,
displayController, pipAnimationController, pipAppOpsListener,
pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler,
- pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
- pipTransitionState, pipTouchHandler, pipTransitionController,
+ pipDisplayLayoutState, pipMotionHelper, pipMediaController, phonePipMenuController,
+ pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
displayInsetsController, oneHandedController, mainExecutor)
.mImpl;
@@ -422,6 +432,7 @@
PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
@NonNull PipBoundsState pipBoundsState,
PipSizeSpecHandler pipSizeSpecHandler,
+ @NonNull PipDisplayLayoutState pipDisplayLayoutState,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
@@ -448,6 +459,7 @@
mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
mPipBoundsState = pipBoundsState;
mPipSizeSpecHandler = pipSizeSpecHandler;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
mPipMotionHelper = pipMotionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
mPipTransitionState = pipTransitionState;
@@ -475,7 +487,7 @@
INPUT_CONSUMER_PIP, mMainExecutor);
mPipTransitionController.registerPipTransitionCallback(this);
mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
- mPipBoundsState.setDisplayId(displayId);
+ mPipDisplayLayoutState.setDisplayId(displayId);
onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
false /* saveRestoreSnapFraction */);
});
@@ -515,11 +527,10 @@
// Ensure that we have the display info in case we get calls to update the bounds before the
// listener calls back
- mPipBoundsState.setDisplayId(mContext.getDisplayId());
+ mPipDisplayLayoutState.setDisplayId(mContext.getDisplayId());
DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay());
- mPipSizeSpecHandler.setDisplayLayout(layout);
- mPipBoundsState.setDisplayLayout(layout);
+ mPipDisplayLayoutState.setDisplayLayout(layout);
try {
mWindowManagerShellWrapper.addPinnedStackListener(mPinnedTaskListener);
@@ -614,12 +625,12 @@
}
});
- mDisplayInsetsController.addInsetsChangedListener(mPipBoundsState.getDisplayId(),
+ mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
new DisplayInsetsController.OnInsetsChangedListener() {
@Override
public void insetsChanged(InsetsState insetsState) {
- DisplayLayout pendingLayout =
- mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId());
+ DisplayLayout pendingLayout = mDisplayController
+ .getDisplayLayout(mPipDisplayLayoutState.getDisplayId());
if (mIsInFixedRotation
|| pendingLayout.rotation()
!= mPipBoundsState.getDisplayLayout().rotation()) {
@@ -627,8 +638,8 @@
return;
}
int oldMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
- onDisplayChangedUncheck(
- mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId()),
+ onDisplayChangedUncheck(mDisplayController
+ .getDisplayLayout(mPipDisplayLayoutState.getDisplayId()),
false /* saveRestoreSnapFraction */);
int newMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
if (!mEnablePipKeepClearAlgorithm) {
@@ -714,7 +725,7 @@
}
private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) {
- if (!mPipBoundsState.getDisplayLayout().isSameGeometry(layout)) {
+ if (!mPipDisplayLayoutState.getDisplayLayout().isSameGeometry(layout)) {
PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getCurrentAnimator();
if (animator != null && animator.isRunning()) {
@@ -728,11 +739,10 @@
private void onDisplayChangedUncheck(DisplayLayout layout, boolean saveRestoreSnapFraction) {
Runnable updateDisplayLayout = () -> {
final boolean fromRotation = Transitions.ENABLE_SHELL_TRANSITIONS
- && mPipBoundsState.getDisplayLayout().rotation() != layout.rotation();
+ && mPipDisplayLayoutState.getDisplayLayout().rotation() != layout.rotation();
// update the internal state of objects subscribed to display changes
- mPipSizeSpecHandler.setDisplayLayout(layout);
- mPipBoundsState.setDisplayLayout(layout);
+ mPipDisplayLayoutState.setDisplayLayout(layout);
final WindowContainerTransaction wct =
fromRotation ? new WindowContainerTransaction() : null;
@@ -756,11 +766,13 @@
mPipBoundsState.getStashedState());
// Scale PiP on density dpi change, so it appears to be the same size physically.
- final boolean densityDpiChanged = mPipBoundsState.getDisplayLayout().densityDpi() != 0
- && (mPipBoundsState.getDisplayLayout().densityDpi() != layout.densityDpi());
+ final boolean densityDpiChanged =
+ mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0
+ && (mPipDisplayLayoutState.getDisplayLayout().densityDpi()
+ != layout.densityDpi());
if (densityDpiChanged) {
final float scale = (float) layout.densityDpi()
- / mPipBoundsState.getDisplayLayout().densityDpi();
+ / mPipDisplayLayoutState.getDisplayLayout().densityDpi();
postChangeBounds.set(0, 0,
(int) (postChangeBounds.width() * scale),
(int) (postChangeBounds.height() * scale));
@@ -775,8 +787,8 @@
pipSnapAlgorithm.applySnapFraction(postChangeBounds, postChangeMovementBounds,
snapFraction, mPipBoundsState.getStashedState(),
mPipBoundsState.getStashOffset(),
- mPipBoundsState.getDisplayBounds(),
- mPipBoundsState.getDisplayLayout().stableInsets());
+ mPipDisplayLayoutState.getDisplayBounds(),
+ mPipDisplayLayoutState.getDisplayLayout().stableInsets());
if (densityDpiChanged) {
// Using PipMotionHelper#movePip directly here may cause race condition since
@@ -874,6 +886,21 @@
}
}
+ private void setLauncherKeepClearAreaHeight(boolean visible, int height) {
+ if (visible) {
+ Rect rect = new Rect(
+ 0, mPipBoundsState.getDisplayBounds().bottom - height,
+ mPipBoundsState.getDisplayBounds().right,
+ mPipBoundsState.getDisplayBounds().bottom);
+ Set<Rect> restrictedKeepClearAreas = new HashSet<>(
+ mPipBoundsState.getRestrictedKeepClearAreas());
+ restrictedKeepClearAreas.add(rect);
+ mPipBoundsState.setKeepClearAreas(restrictedKeepClearAreas,
+ mPipBoundsState.getUnrestrictedKeepClearAreas());
+ updatePipPositionForKeepClearAreas();
+ }
+ }
+
private void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {
mOnIsInPipStateChangedListener = callback;
if (mOnIsInPipStateChangedListener != null) {
@@ -1018,7 +1045,7 @@
// Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler before
// passing to mTouchHandler/mPipTaskOrganizer
final Rect outBounds = new Rect(toBounds);
- final int rotation = mPipBoundsState.getDisplayLayout().rotation();
+ final int rotation = mPipDisplayLayoutState.getDisplayLayout().rotation();
mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds());
@@ -1042,11 +1069,7 @@
private void onDisplayRotationChangedNotInPip(Context context, int toRotation) {
// Update the display layout, note that we have to do this on every rotation even if we
// aren't in PIP since we need to update the display layout to get the right resources
- DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
-
- layoutCopy.rotateTo(context.getResources(), toRotation);
- mPipBoundsState.setDisplayLayout(layoutCopy);
- mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+ mPipDisplayLayoutState.rotateTo(toRotation);
}
/**
@@ -1059,7 +1082,7 @@
Rect outInsetBounds,
int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) {
// Bail early if the event is not sent to current display
- if ((displayId != mPipBoundsState.getDisplayId()) || (fromRotation == toRotation)) {
+ if ((displayId != mPipDisplayLayoutState.getDisplayId()) || (fromRotation == toRotation)) {
return false;
}
@@ -1083,11 +1106,7 @@
mPipBoundsState.getStashedState());
// Update the display layout
- DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
-
- layoutCopy.rotateTo(context.getResources(), toRotation);
- mPipBoundsState.setDisplayLayout(layoutCopy);
- mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+ mPipDisplayLayoutState.rotateTo(toRotation);
// Calculate the stack bounds in the new orientation based on same fraction along the
// rotated movement bounds.
@@ -1095,8 +1114,8 @@
postChangeStackBounds, false /* adjustForIme */);
pipSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
snapFraction, mPipBoundsState.getStashedState(), mPipBoundsState.getStashOffset(),
- mPipBoundsState.getDisplayBounds(),
- mPipBoundsState.getDisplayLayout().stableInsets());
+ mPipDisplayLayoutState.getDisplayBounds(),
+ mPipDisplayLayoutState.getDisplayLayout().stableInsets());
mPipBoundsAlgorithm.getInsetBounds(outInsetBounds);
outBounds.set(postChangeStackBounds);
@@ -1114,6 +1133,7 @@
mPipBoundsState.dump(pw, innerPrefix);
mPipInputConsumer.dump(pw, innerPrefix);
mPipSizeSpecHandler.dump(pw, innerPrefix);
+ mPipDisplayLayoutState.dump(pw, innerPrefix);
}
/**
@@ -1240,6 +1260,14 @@
}
@Override
+ public void setLauncherKeepClearAreaHeight(boolean visible, int height) {
+ executeRemoteCallWithTaskPermission(mController, "setLauncherKeepClearAreaHeight",
+ (controller) -> {
+ controller.setLauncherKeepClearAreaHeight(visible, height);
+ });
+ }
+
+ @Override
public void setPipAnimationListener(IPipAnimationListener listener) {
executeRemoteCallWithTaskPermission(mController, "setPipAnimationListener",
(controller) -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
index d03d075..23988a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
@@ -31,6 +31,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import java.io.PrintWriter;
@@ -40,10 +41,9 @@
public class PipSizeSpecHandler {
private static final String TAG = PipSizeSpecHandler.class.getSimpleName();
- @NonNull private final DisplayLayout mDisplayLayout = new DisplayLayout();
+ @NonNull private final PipDisplayLayoutState mPipDisplayLayoutState;
- @VisibleForTesting
- final SizeSpecSource mSizeSpecSourceImpl;
+ private final SizeSpecSource mSizeSpecSourceImpl;
/** The preferred minimum (and default minimum) size specified by apps. */
@Nullable private Size mOverrideMinSize;
@@ -361,8 +361,9 @@
}
}
- public PipSizeSpecHandler(Context context) {
+ public PipSizeSpecHandler(Context context, PipDisplayLayoutState pipDisplayLayoutState) {
mContext = context;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
boolean enablePipSizeLargeScreen = SystemProperties
.getBoolean("persist.wm.debug.enable_pip_size_large_screen", false);
@@ -403,15 +404,9 @@
mSizeSpecSourceImpl.reloadResources();
}
- /** Returns the display's bounds. */
@NonNull
- public Rect getDisplayBounds() {
- return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
- }
-
- /** Update the display layout. */
- public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
- mDisplayLayout.set(displayLayout);
+ private Rect getDisplayBounds() {
+ return mPipDisplayLayoutState.getDisplayBounds();
}
public Point getScreenEdgeInsets() {
@@ -423,11 +418,12 @@
*/
public Rect getInsetBounds() {
Rect insetBounds = new Rect();
- Rect insets = mDisplayLayout.stableInsets();
+ DisplayLayout displayLayout = mPipDisplayLayoutState.getDisplayLayout();
+ Rect insets = displayLayout.stableInsets();
insetBounds.set(insets.left + mScreenEdgeInsets.x,
insets.top + mScreenEdgeInsets.y,
- mDisplayLayout.width() - insets.right - mScreenEdgeInsets.x,
- mDisplayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
+ displayLayout.width() - insets.right - mScreenEdgeInsets.x,
+ displayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
return insetBounds;
}
@@ -522,8 +518,8 @@
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
- pw.println(innerPrefix + "mSizeSpecSourceImpl=" + mSizeSpecSourceImpl.toString());
- pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
+ pw.println(innerPrefix + "mSizeSpecSourceImpl=" + mSizeSpecSourceImpl);
pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize);
+ pw.println(innerPrefix + "mScreenEdgeInsets=" + mScreenEdgeInsets);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index 22b3f49..e1737ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -31,6 +31,7 @@
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import java.lang.annotation.Retention;
@@ -75,8 +76,9 @@
private Insets mPipMenuTemporaryDecorInsets = Insets.NONE;
public TvPipBoundsState(@NonNull Context context,
- @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
- super(context, pipSizeSpecHandler);
+ @NonNull PipSizeSpecHandler pipSizeSpecHandler,
+ @NonNull PipDisplayLayoutState pipDisplayLayoutState) {
+ super(context, pipSizeSpecHandler, pipDisplayLayoutState);
mContext = context;
updateDefaultGravity();
mPreviousCollapsedGravity = mDefaultGravity;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index a437a3b..d73723c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -51,11 +51,11 @@
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipAppOpsListener;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellController;
@@ -119,7 +119,7 @@
private final ShellController mShellController;
private final TvPipBoundsState mTvPipBoundsState;
- private final PipSizeSpecHandler mPipSizeSpecHandler;
+ private final PipDisplayLayoutState mPipDisplayLayoutState;
private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
private final TvPipBoundsController mTvPipBoundsController;
private final PipAppOpsListener mAppOpsListener;
@@ -154,7 +154,7 @@
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
- PipSizeSpecHandler pipSizeSpecHandler,
+ PipDisplayLayoutState pipDisplayLayoutState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
PipAppOpsListener pipAppOpsListener,
@@ -174,7 +174,7 @@
shellInit,
shellController,
tvPipBoundsState,
- pipSizeSpecHandler,
+ pipDisplayLayoutState,
tvPipBoundsAlgorithm,
tvPipBoundsController,
pipAppOpsListener,
@@ -196,7 +196,7 @@
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
- PipSizeSpecHandler pipSizeSpecHandler,
+ PipDisplayLayoutState pipDisplayLayoutState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
PipAppOpsListener pipAppOpsListener,
@@ -220,10 +220,11 @@
DisplayLayout layout = new DisplayLayout(context, context.getDisplay());
mTvPipBoundsState = tvPipBoundsState;
- mTvPipBoundsState.setDisplayLayout(layout);
- mTvPipBoundsState.setDisplayId(context.getDisplayId());
- mPipSizeSpecHandler = pipSizeSpecHandler;
- mPipSizeSpecHandler.setDisplayLayout(layout);
+
+ mPipDisplayLayoutState = pipDisplayLayoutState;
+ mPipDisplayLayoutState.setDisplayLayout(layout);
+ mPipDisplayLayoutState.setDisplayId(context.getDisplayId());
+
mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
mTvPipBoundsController = tvPipBoundsController;
mTvPipBoundsController.setListener(this);
@@ -392,7 +393,7 @@
@Override
public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
Set<Rect> unrestricted) {
- if (mTvPipBoundsState.getDisplayId() == displayId) {
+ if (mPipDisplayLayoutState.getDisplayId() == displayId) {
boolean unrestrictedAreasChanged = !Objects.equals(unrestricted,
mTvPipBoundsState.getUnrestrictedKeepClearAreas());
mTvPipBoundsState.setKeepClearAreas(restricted, unrestricted);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index be9b936..f6856f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -28,6 +28,7 @@
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
@@ -36,7 +37,6 @@
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipUtils;
-import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.Objects;
@@ -51,7 +51,7 @@
@NonNull SyncTransactionQueue syncTransactionQueue,
@NonNull PipTransitionState pipTransitionState,
@NonNull PipBoundsState pipBoundsState,
- @NonNull PipSizeSpecHandler pipSizeSpecHandler,
+ @NonNull PipDisplayLayoutState pipDisplayLayoutState,
@NonNull PipBoundsAlgorithm boundsHandler,
@NonNull PipMenuController pipMenuController,
@NonNull PipAnimationController pipAnimationController,
@@ -63,8 +63,8 @@
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
ShellExecutor mainExecutor) {
- super(context, syncTransactionQueue, pipTransitionState, pipBoundsState, pipSizeSpecHandler,
- boundsHandler, pipMenuController, pipAnimationController,
+ super(context, syncTransactionQueue, pipTransitionState, pipBoundsState,
+ pipDisplayLayoutState, boundsHandler, pipMenuController, pipAnimationController,
surfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer,
mainExecutor);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 18a3849..a5546e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -207,6 +207,7 @@
private boolean mIsDividerRemoteAnimating;
private boolean mIsDropEntering;
private boolean mIsExiting;
+ private boolean mIsRootTranslucent;
private DefaultMixedHandler mMixedHandler;
private final Toast mSplitUnsupportedToast;
@@ -422,6 +423,11 @@
}
}
+ if (!isSplitActive()) {
+ // prevent the fling divider to center transitioni if split screen didn't active.
+ mIsDropEntering = true;
+ }
+
setSideStagePosition(sideStagePosition, wct);
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
targetStage.evictAllChildren(evictWct);
@@ -436,28 +442,13 @@
// reparent the task to an invisible split root will make the activity invisible. Reorder
// the root task to front to make the entering transition from pip to split smooth.
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, true);
wct.reorder(targetStage.mRootTaskInfo.token, true);
- wct.setForceTranslucent(targetStage.mRootTaskInfo.token, true);
- // prevent the fling divider to center transition
- mIsDropEntering = true;
-
targetStage.addTask(task, wct);
- if (ENABLE_SHELL_TRANSITIONS) {
- prepareEnterSplitScreen(wct);
- mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct,
- null, this, null /* consumedCallback */, (finishWct, finishT) -> {
- if (!evictWct.isEmpty()) {
- finishWct.merge(evictWct, true);
- }
- } /* finishedCallback */);
- } else {
- if (!evictWct.isEmpty()) {
- wct.merge(evictWct, true /* transfer */);
- }
- mTaskOrganizer.applyTransaction(wct);
+ if (!evictWct.isEmpty()) {
+ wct.merge(evictWct, true /* transfer */);
}
+ mTaskOrganizer.applyTransaction(wct);
return true;
}
@@ -716,7 +707,7 @@
mSplitLayout.setDivideRatio(splitRatio);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ setRootForceTranslucent(false, wct);
// Make sure the launch options will put tasks in the corresponding split roots
mainOptions = mainOptions != null ? mainOptions : new Bundle();
@@ -923,7 +914,7 @@
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ setRootForceTranslucent(false, wct);
// TODO(b/268008375): Merge APIs to start a split pair into one.
if (mainTaskId != INVALID_TASK_ID) {
@@ -1351,7 +1342,7 @@
mSideStage.removeAllTasks(wct, false /* toTop */);
mMainStage.deactivate(wct, false /* toTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
- wct.setForceTranslucent(mRootTaskInfo.token, true);
+ setRootForceTranslucent(true, wct);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
onTransitionAnimationComplete();
} else {
@@ -1383,7 +1374,7 @@
mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
- finishedWCT.setForceTranslucent(mRootTaskInfo.token, true);
+ setRootForceTranslucent(true, wct);
finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
mSyncQueue.queue(finishedWCT);
mSyncQueue.runInSync(at -> {
@@ -1505,7 +1496,7 @@
mMainStage.activate(wct, true /* includingTopTask */);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ setRootForceTranslucent(false, wct);
}
void finishEnterSplitScreen(SurfaceControl.Transaction t) {
@@ -1709,6 +1700,7 @@
mRootTaskInfo = null;
mRootTaskLeash = null;
+ mIsRootTranslucent = false;
}
@@ -1727,7 +1719,7 @@
// Make the stages adjacent to each other so they occlude what's behind them.
wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
- wct.setForceTranslucent(mRootTaskInfo.token, true);
+ setRootForceTranslucent(true, wct);
mSplitLayout.getInvisibleBounds(mTempRect1);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
mSyncQueue.queue(wct);
@@ -1751,7 +1743,7 @@
mSideStage.evictOtherChildren(wct, taskId);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ setRootForceTranslucent(false, wct);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
@@ -1775,6 +1767,13 @@
mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, mSplitLayout);
}
+ private void setRootForceTranslucent(boolean translucent, WindowContainerTransaction wct) {
+ if (mIsRootTranslucent == translucent) return;
+
+ mIsRootTranslucent = translucent;
+ wct.setForceTranslucent(mRootTaskInfo.token, translucent);
+ }
+
private void onStageVisibilityChanged(StageListenerImpl stageListener) {
// If split didn't active, just ignore this callback because we should already did these
// on #applyExitSplitScreen.
@@ -1801,12 +1800,11 @@
// Split entering background.
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
true /* setReparentLeafTaskIfRelaunch */);
- if (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping) {
- wct.setForceTranslucent(mRootTaskInfo.token, true);
- }
+ setRootForceTranslucent(true, wct);
} else {
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
false /* setReparentLeafTaskIfRelaunch */);
+ setRootForceTranslucent(false, wct);
}
mSyncQueue.queue(wct);
@@ -1938,7 +1936,7 @@
mMainStage.activate(wct, true /* includingTopTask */);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ setRootForceTranslucent(false, wct);
}
mSyncQueue.queue(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 9224b3c..6b7ca42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -193,6 +193,7 @@
private final DragDetector mDragDetector;
private int mDragPointerId = -1;
+ private boolean mIsDragging;
private CaptionTouchEventListener(
RunningTaskInfo taskInfo,
@@ -223,19 +224,15 @@
if (v.getId() != R.id.caption) {
return false;
}
- mDragDetector.onMotionEvent(e);
-
- if (e.getAction() != MotionEvent.ACTION_DOWN) {
- return false;
+ if (e.getAction() == MotionEvent.ACTION_DOWN) {
+ final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ if (!taskInfo.isFocused) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reorder(mTaskToken, true /* onTop */);
+ mSyncQueue.queue(wct);
+ }
}
- final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- if (taskInfo.isFocused) {
- return false;
- }
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reorder(mTaskToken, true /* onTop */);
- mSyncQueue.queue(wct);
- return true;
+ return mDragDetector.onMotionEvent(e);
}
/**
@@ -253,20 +250,24 @@
mDragPointerId = e.getPointerId(0);
mDragPositioningCallback.onDragPositioningStart(
0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
- break;
+ mIsDragging = false;
+ return false;
}
case MotionEvent.ACTION_MOVE: {
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
- break;
+ mIsDragging = true;
+ return true;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
- break;
+ final boolean wasDragging = mIsDragging;
+ mIsDragging = false;
+ return wasDragging;
}
}
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 2aa6d12..6b45149 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -223,6 +223,7 @@
private final DragPositioningCallback mDragPositioningCallback;
private final DragDetector mDragDetector;
+ private boolean mIsDragging;
private int mDragPointerId = -1;
private DesktopModeTouchEventListener(
@@ -273,23 +274,7 @@
if (id != R.id.caption_handle && id != R.id.desktop_mode_caption) {
return false;
}
- switch (e.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mDragDetector.onMotionEvent(e);
- final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- if (taskInfo.isFocused) {
- return mDragDetector.isDragEvent();
- }
- return false;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- boolean res = mDragDetector.isDragEvent();
- mDragDetector.onMotionEvent(e);
- return res;
- default:
- mDragDetector.onMotionEvent(e);
- return mDragDetector.isDragEvent();
- }
+ return mDragDetector.onMotionEvent(e);
}
/**
@@ -313,13 +298,15 @@
mDragPointerId = e.getPointerId(0);
mDragPositioningCallback.onDragPositioningStart(
0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
- break;
+ mIsDragging = false;
+ return false;
}
case MotionEvent.ACTION_MOVE: {
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
- break;
+ mIsDragging = true;
+ return true;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
@@ -336,7 +323,9 @@
c -> c.moveToFullscreen(taskInfo));
}
}
- break;
+ final boolean wasDragging = mIsDragging;
+ mIsDragging = false;
+ return wasDragging;
}
}
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index cf1850b..65b5a7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -56,10 +56,15 @@
* {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
*/
boolean onMotionEvent(MotionEvent ev) {
+ final boolean isTouchScreen =
+ (ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
+ if (!isTouchScreen) {
+ // Only touches generate noisy moves, so mouse/trackpad events don't need to filtered
+ // to take the slop threshold into consideration.
+ return mEventHandler.handleMotionEvent(ev);
+ }
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
- // Only touch screens generate noisy moves.
- mIsDragEvent = (ev.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN;
mDragPointerId = ev.getPointerId(0);
float rawX = ev.getRawX(0);
float rawY = ev.getRawY(0);
@@ -72,8 +77,12 @@
int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
+ // Touches generate noisy moves, so only once the move is past the touch
+ // slop threshold should it be considered a drag.
mIsDragEvent = Math.hypot(dx, dy) > mTouchSlop;
}
+ // The event handler should only be notified about 'move' events if a drag has been
+ // detected.
if (mIsDragEvent) {
return mEventHandler.handleMotionEvent(ev);
} else {
@@ -94,10 +103,6 @@
mTouchSlop = touchSlop;
}
- boolean isDragEvent() {
- return mIsDragEvent;
- }
-
private void resetState() {
mIsDragEvent = false;
mInputDownPoint.set(0, 0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index a3d364a..0bce3ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -40,6 +40,7 @@
private final DisplayController mDisplayController;
private final WindowDecoration mWindowDecoration;
+ private final Rect mTempBounds = new Rect();
private final Rect mTaskBoundsAtDragStart = new Rect();
private final PointF mRepositionStartPoint = new PointF();
private final Rect mRepositionTaskBounds = new Rect();
@@ -117,17 +118,32 @@
final float deltaX = x - mRepositionStartPoint.x;
final float deltaY = y - mRepositionStartPoint.y;
mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
+
+ final Rect stableBounds = mTempBounds;
+ // Make sure the new resizing destination in any direction falls within the stable bounds.
+ // If not, set the bounds back to the old location that was valid to avoid conflicts with
+ // some regions such as the gesture area.
+ mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId())
+ .getStableBounds(stableBounds);
if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
- mRepositionTaskBounds.left += deltaX;
+ final int candidateLeft = mRepositionTaskBounds.left + (int) deltaX;
+ mRepositionTaskBounds.left = (candidateLeft > stableBounds.left)
+ ? candidateLeft : oldLeft;
}
if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
- mRepositionTaskBounds.right += deltaX;
+ final int candidateRight = mRepositionTaskBounds.right + (int) deltaX;
+ mRepositionTaskBounds.right = (candidateRight < stableBounds.right)
+ ? candidateRight : oldRight;
}
if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
- mRepositionTaskBounds.top += deltaY;
+ final int candidateTop = mRepositionTaskBounds.top + (int) deltaY;
+ mRepositionTaskBounds.top = (candidateTop > stableBounds.top)
+ ? candidateTop : oldTop;
}
if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
- mRepositionTaskBounds.bottom += deltaY;
+ final int candidateBottom = mRepositionTaskBounds.bottom + (int) deltaY;
+ mRepositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom)
+ ? candidateBottom : oldBottom;
}
if (mCtrlType == CTRL_TYPE_UNDEFINED) {
mRepositionTaskBounds.offset((int) deltaX, (int) deltaY);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
new file mode 100644
index 0000000..5cd66ab
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.graphics.Rect
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test the snapping of a PIP window via dragging, releasing, and checking its final location.
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker){
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ val stringExtras: Map<String, String> =
+ mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")
+
+ // cache the starting bounds here
+ val startBounds = Rect()
+
+ setup {
+ // Launch the PIP activity and wait for it to enter PiP mode
+ setRotation(Rotation.ROTATION_0)
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
+
+ val initRegion = pipApp.dragPipWindowAwayFromEdge(wmHelper, 50)
+ startBounds
+ .set(initRegion.left, initRegion.top, initRegion.right, initRegion.bottom)
+ }
+ transitions {
+ // continue the transition until the PIP snaps
+ pipApp.waitForPipToSnapTo(wmHelper, startBounds)
+ }
+ }
+
+ /** Checks that the visible region area of [pipApp] always moves right during the animation. */
+ @Postsubmit
+ @Test
+ fun pipLayerMovesRight() {
+ flicker.assertLayers {
+ val pipLayerList = layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+ pipLayerList.zipWithNext { previous, current ->
+ current.visibleRegion.isToTheRight(previous.visibleRegion.region)
+ }
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index ec264a6..addc233 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -55,25 +55,28 @@
private static final float MAX_ASPECT_RATIO = 2f;
private static final int DEFAULT_MIN_EDGE_SIZE = 100;
+ /** The minimum possible size of the override min size's width or height */
+ private static final int OVERRIDABLE_MIN_SIZE = 40;
+
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private DisplayInfo mDefaultDisplayInfo;
- private PipBoundsState mPipBoundsState;
- private PipSizeSpecHandler mPipSizeSpecHandler;
+ private PipBoundsState mPipBoundsState; private PipSizeSpecHandler mPipSizeSpecHandler;
+ private PipDisplayLayoutState mPipDisplayLayoutState;
@Before
public void setUp() throws Exception {
initializeMockResources();
- mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
- mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
+ mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState);
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {},
mPipSizeSpecHandler);
DisplayLayout layout =
new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true);
- mPipBoundsState.setDisplayLayout(layout);
- mPipSizeSpecHandler.setDisplayLayout(layout);
+ mPipDisplayLayoutState.setDisplayLayout(layout);
}
private void initializeMockResources() {
@@ -88,6 +91,9 @@
R.dimen.default_minimal_size_pip_resizable_task,
DEFAULT_MIN_EDGE_SIZE);
res.addOverride(
+ R.dimen.overridable_minimal_size_pip_resizable_task,
+ OVERRIDABLE_MIN_SIZE);
+ res.addOverride(
R.string.config_defaultPictureInPictureScreenEdgeInsets,
"16x16");
res.addOverride(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index 341a451..f320004 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -27,11 +27,13 @@
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.testing.TestableResources;
import android.util.Size;
import androidx.test.filters.SmallTest;
import com.android.internal.util.function.TriConsumer;
+import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
@@ -52,13 +54,23 @@
private static final Size DEFAULT_SIZE = new Size(10, 10);
private static final float DEFAULT_SNAP_FRACTION = 1.0f;
+ /** The minimum possible size of the override min size's width or height */
+ private static final int OVERRIDABLE_MIN_SIZE = 40;
+
private PipBoundsState mPipBoundsState;
private ComponentName mTestComponentName1;
private ComponentName mTestComponentName2;
@Before
public void setUp() {
- mPipBoundsState = new PipBoundsState(mContext, new PipSizeSpecHandler(mContext));
+ final TestableResources res = mContext.getOrCreateTestableResources();
+ res.addOverride(
+ R.dimen.overridable_minimal_size_pip_resizable_task,
+ OVERRIDABLE_MIN_SIZE);
+
+ PipDisplayLayoutState pipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+ mPipBoundsState = new PipBoundsState(mContext,
+ new PipSizeSpecHandler(mContext, pipDisplayLayoutState), pipDisplayLayoutState);
mTestComponentName1 = new ComponentName(mContext, "component1");
mTestComponentName2 = new ComponentName(mContext, "component2");
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index e907cd3..15bb10e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -88,6 +88,7 @@
private PipTransitionState mPipTransitionState;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private PipSizeSpecHandler mPipSizeSpecHandler;
+ private PipDisplayLayoutState mPipDisplayLayoutState;
private ComponentName mComponent1;
private ComponentName mComponent2;
@@ -97,15 +98,16 @@
MockitoAnnotations.initMocks(this);
mComponent1 = new ComponentName(mContext, "component1");
mComponent2 = new ComponentName(mContext, "component2");
- mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
- mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
+ mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState);
mPipTransitionState = new PipTransitionState();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {},
mPipSizeSpecHandler);
mMainExecutor = new TestShellExecutor();
mPipTaskOrganizer = new PipTaskOrganizer(mContext, mMockSyncTransactionQueue,
- mPipTransitionState, mPipBoundsState, mPipSizeSpecHandler,
+ mPipTransitionState, mPipBoundsState, mPipDisplayLayoutState,
mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController,
mMockPipSurfaceTransactionHelper, mMockPipTransitionController,
mMockPipParamsChangedForwarder, mMockOptionalSplitScreen, mMockDisplayController,
@@ -259,8 +261,7 @@
final DisplayInfo info = new DisplayInfo();
DisplayLayout layout = new DisplayLayout(info,
mContext.getResources(), true, true);
- mPipBoundsState.setDisplayLayout(layout);
- mPipSizeSpecHandler.setDisplayLayout(layout);
+ mPipDisplayLayoutState.setDisplayLayout(layout);
mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
mPipTaskOrganizer.setSurfaceControlTransactionFactory(
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 4a68287..0e14c69 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -59,6 +59,7 @@
import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -108,6 +109,7 @@
@Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
@Mock private PipBoundsState mMockPipBoundsState;
@Mock private PipSizeSpecHandler mMockPipSizeSpecHandler;
+ @Mock private PipDisplayLayoutState mMockPipDisplayLayoutState;
@Mock private TaskStackListenerImpl mMockTaskStackListener;
@Mock private ShellExecutor mMockExecutor;
@Mock private Optional<OneHandedController> mMockOneHandedController;
@@ -130,12 +132,12 @@
mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
mShellController, mMockDisplayController, mMockPipAnimationController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
- mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipMotionHelper,
- mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
- mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
- mMockWindowManagerShellWrapper, mMockTaskStackListener,
- mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
- mMockOneHandedController, mMockExecutor);
+ mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipDisplayLayoutState,
+ mMockPipMotionHelper, mMockPipMediaController, mMockPhonePipMenuController,
+ mMockPipTaskOrganizer, mMockPipTransitionState, mMockPipTouchHandler,
+ mMockPipTransitionController, mMockWindowManagerShellWrapper,
+ mMockTaskStackListener, mMockPipParamsChangedForwarder,
+ mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor);
mShellInit.init();
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -221,12 +223,12 @@
assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
mShellController, mMockDisplayController, mMockPipAnimationController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
- mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipMotionHelper,
- mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
- mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
- mMockWindowManagerShellWrapper, mMockTaskStackListener,
- mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
- mMockOneHandedController, mMockExecutor));
+ mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipDisplayLayoutState,
+ mMockPipMotionHelper, mMockPipMediaController, mMockPhonePipMenuController,
+ mMockPipTaskOrganizer, mMockPipTransitionState, mMockPipTouchHandler,
+ mMockPipTransitionController, mMockWindowManagerShellWrapper,
+ mMockTaskStackListener, mMockPipParamsChangedForwarder,
+ mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor));
}
@Test
@@ -283,8 +285,8 @@
when(mMockPipBoundsState.getMinSize()).thenReturn(new Point(1, 1));
when(mMockPipBoundsState.getMaxSize()).thenReturn(new Point(MAX_VALUE, MAX_VALUE));
when(mMockPipBoundsState.getBounds()).thenReturn(bounds);
- when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
- when(mMockPipBoundsState.getDisplayLayout()).thenReturn(mMockDisplayLayout1);
+ when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId);
+ when(mMockPipDisplayLayoutState.getDisplayLayout()).thenReturn(mMockDisplayLayout1);
when(mMockDisplayController.getDisplayLayout(displayId)).thenReturn(mMockDisplayLayout2);
when(mMockPipTaskOrganizer.isInPip()).thenReturn(true);
@@ -299,8 +301,8 @@
final int displayId = 1;
final Rect bounds = new Rect(0, 0, 10, 10);
when(mMockPipBoundsAlgorithm.getDefaultBounds()).thenReturn(bounds);
- when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
- when(mMockPipBoundsState.getDisplayLayout()).thenReturn(mMockDisplayLayout1);
+ when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId);
+ when(mMockPipDisplayLayoutState.getDisplayLayout()).thenReturn(mMockDisplayLayout1);
when(mMockDisplayController.getDisplayLayout(displayId)).thenReturn(mMockDisplayLayout2);
when(mMockPipTaskOrganizer.isInPip()).thenReturn(false);
@@ -314,7 +316,7 @@
public void onKeepClearAreasChanged_featureDisabled_pipBoundsStateDoesntChange() {
final int displayId = 1;
final Rect keepClearArea = new Rect(0, 0, 10, 10);
- when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
+ when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId);
mPipController.mDisplaysChangedListener.onKeepClearAreasChanged(
displayId, Set.of(keepClearArea), Set.of());
@@ -327,7 +329,7 @@
mPipController.setEnablePipKeepClearAlgorithm(true);
final int displayId = 1;
final Rect keepClearArea = new Rect(0, 0, 10, 10);
- when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
+ when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId);
mPipController.mDisplaysChangedListener.onKeepClearAreasChanged(
displayId, Set.of(keepClearArea), Set.of());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index c7b9eb3..5b62a94 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -37,6 +37,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -87,11 +88,14 @@
private PipSizeSpecHandler mPipSizeSpecHandler;
+ private PipDisplayLayoutState mPipDisplayLayoutState;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
- mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
+ mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState);
final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
final PipKeepClearAlgorithmInterface pipKeepClearAlgorithm =
new PipKeepClearAlgorithmInterface() {};
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
index d9ff7d1..390c830 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
@@ -33,6 +33,7 @@
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import org.junit.After;
import org.junit.Assert;
@@ -74,6 +75,7 @@
@Mock private Context mContext;
@Mock private Resources mResources;
+ private PipDisplayLayoutState mPipDisplayLayoutState;
private PipSizeSpecHandler mPipSizeSpecHandler;
/**
@@ -137,7 +139,6 @@
@Before
public void setUp() {
initExpectedSizes();
- setUpStaticSystemPropertiesSession();
when(mResources.getDimensionPixelSize(anyInt())).thenReturn(DEFAULT_MIN_EDGE_SIZE);
when(mResources.getFloat(anyInt())).thenReturn(OPTIMIZED_ASPECT_RATIO);
@@ -148,11 +149,6 @@
// set up the mock context for spec handler specifically
when(mContext.getResources()).thenReturn(mResources);
- mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
-
- // no overridden min edge size by default
- mPipSizeSpecHandler.setOverrideMinSize(null);
-
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = DISPLAY_EDGE_SIZE;
displayInfo.logicalHeight = DISPLAY_EDGE_SIZE;
@@ -161,7 +157,14 @@
// this is done to avoid unnecessary mocking while allowing for custom display dimensions
DisplayLayout displayLayout = new DisplayLayout(displayInfo, getContext().getResources(),
false, false);
- mPipSizeSpecHandler.setDisplayLayout(displayLayout);
+ mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+ mPipDisplayLayoutState.setDisplayLayout(displayLayout);
+
+ setUpStaticSystemPropertiesSession();
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+
+ // no overridden min edge size by default
+ mPipSizeSpecHandler.setOverrideMinSize(null);
}
@After
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 5c4863f..d36060f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -35,6 +35,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -92,6 +93,7 @@
private PipMotionHelper mMotionHelper;
private PipResizeGestureHandler mPipResizeGestureHandler;
private PipSizeSpecHandler mPipSizeSpecHandler;
+ private PipDisplayLayoutState mPipDisplayLayoutState;
private DisplayLayout mDisplayLayout;
private Rect mInsetBounds;
@@ -105,8 +107,9 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
- mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
+ mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState);
mPipSnapAlgorithm = new PipSnapAlgorithm();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm,
new PipKeepClearAlgorithmInterface() {}, mPipSizeSpecHandler);
@@ -124,8 +127,7 @@
mPipTouchHandler.setPipResizeGestureHandler(mPipResizeGestureHandler);
mDisplayLayout = new DisplayLayout(mContext, mContext.getDisplay());
- mPipBoundsState.setDisplayLayout(mDisplayLayout);
- mPipSizeSpecHandler.setDisplayLayout(mDisplayLayout);
+ mPipDisplayLayoutState.setDisplayLayout(mDisplayLayout);
mInsetBounds = new Rect(mPipBoundsState.getDisplayBounds().left + INSET,
mPipBoundsState.getDisplayBounds().top + INSET,
mPipBoundsState.getDisplayBounds().right - INSET,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
index 30096cb..f9b7723 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
@@ -26,6 +26,7 @@
import android.view.Gravity;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
@@ -47,6 +48,7 @@
private TvPipBoundsState mTvPipBoundsState;
private TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
private PipSizeSpecHandler mPipSizeSpecHandler;
+ private PipDisplayLayoutState mPipDisplayLayoutState;
@Before
public void setUp() {
@@ -54,8 +56,10 @@
return;
}
MockitoAnnotations.initMocks(this);
- mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
- mTvPipBoundsState = new TvPipBoundsState(mContext, mPipSizeSpecHandler);
+ mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+ mTvPipBoundsState = new TvPipBoundsState(mContext, mPipSizeSpecHandler,
+ mPipDisplayLayoutState);
mTvPipBoundsAlgorithm = new TvPipBoundsAlgorithm(mContext, mTvPipBoundsState,
mMockPipSnapAlgorithm, mPipSizeSpecHandler);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
index 8f66f4e..94c064b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -5,13 +5,16 @@
import android.graphics.Rect
import android.os.IBinder
import android.testing.AndroidTestingRunner
+import android.view.Display
import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
import androidx.test.filters.SmallTest
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_BOTTOM
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
@@ -19,10 +22,11 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
import org.mockito.Mockito.argThat
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
/**
@@ -51,6 +55,8 @@
private lateinit var mockDisplayController: DisplayController
@Mock
private lateinit var mockDisplayLayout: DisplayLayout
+ @Mock
+ private lateinit var mockDisplay: Display
private lateinit var taskPositioner: TaskPositioner
@@ -68,6 +74,9 @@
`when`(taskToken.asBinder()).thenReturn(taskBinder)
`when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
`when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
+ `when`(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
taskId = TASK_ID
@@ -78,6 +87,8 @@
displayId = DISPLAY_ID
configuration.windowConfiguration.bounds = STARTING_BOUNDS
}
+ mockWindowDecoration.mDisplay = mockDisplay
+ `when`(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
}
@Test
@@ -451,6 +462,72 @@
})
}
+ fun testDragResize_toDisallowedBounds_freezesAtLimit() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, // Resize right-bottom corner
+ STARTING_BOUNDS.right.toFloat(),
+ STARTING_BOUNDS.bottom.toFloat()
+ )
+
+ // Resize the task by 10px to the right and bottom, a valid destination
+ val newBounds = Rect(
+ STARTING_BOUNDS.left,
+ STARTING_BOUNDS.top,
+ STARTING_BOUNDS.right + 10,
+ STARTING_BOUNDS.bottom + 10)
+ taskPositioner.onDragPositioningMove(
+ newBounds.right.toFloat(),
+ newBounds.bottom.toFloat()
+ )
+
+ // Resize the task by another 10px to the right (allowed) and to just in the disallowed
+ // area of the Y coordinate.
+ val newBounds2 = Rect(
+ newBounds.left,
+ newBounds.top,
+ newBounds.right + 10,
+ DISALLOWED_RESIZE_AREA.top
+ )
+ taskPositioner.onDragPositioningMove(
+ newBounds2.right.toFloat(),
+ newBounds2.bottom.toFloat()
+ )
+
+ taskPositioner.onDragPositioningEnd(newBounds2.right.toFloat(), newBounds2.bottom.toFloat())
+
+ // The first resize falls in the allowed area, verify there's a change for it.
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder && change.ofBounds(newBounds)
+ }
+ })
+ // The second resize falls in the disallowed area, verify there's no change for it.
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder && change.ofBounds(newBounds2)
+ }
+ })
+ // Instead, there should be a change for its allowed portion (the X movement) with the Y
+ // staying frozen in the last valid resize position.
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder && change.ofBounds(
+ Rect(
+ newBounds2.left,
+ newBounds2.top,
+ newBounds2.right,
+ newBounds.bottom // Stayed at the first resize destination.
+ )
+ )
+ }
+ })
+ }
+
+ private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean {
+ return ((windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) &&
+ bounds == configuration.windowConfiguration.bounds
+ }
+
companion object {
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
@@ -458,6 +535,19 @@
private const val DENSITY_DPI = 20
private const val DEFAULT_MIN = 40
private const val DISPLAY_ID = 1
+ private const val NAVBAR_HEIGHT = 50
+ private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
+ private val DISALLOWED_RESIZE_AREA = Rect(
+ DISPLAY_BOUNDS.left,
+ DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
+ DISPLAY_BOUNDS.right,
+ DISPLAY_BOUNDS.bottom)
+ private val STABLE_BOUNDS = Rect(
+ DISPLAY_BOUNDS.left,
+ DISPLAY_BOUNDS.top,
+ DISPLAY_BOUNDS.right,
+ DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
+ )
}
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4da4c7f..f9b6ce0 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -9564,7 +9564,10 @@
* A stream type is considered independent when the volume changes of that type do not
* affect any other independent volume control stream type.
* An independent stream type is its own alias when using {@link #getStreamTypeAlias(int)}.
- * @return list of independent stream types.
+ * @return list of independent stream types, where each value can be one of
+ * {@link #STREAM_VOICE_CALL}, {@link #STREAM_SYSTEM}, {@link #STREAM_RING},
+ * {@link #STREAM_MUSIC}, {@link #STREAM_ALARM}, {@link #STREAM_NOTIFICATION},
+ * {@link #STREAM_DTMF} and {@link #STREAM_ACCESSIBILITY}.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index c52cd59..0f8a00a 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -55,7 +55,7 @@
void onCueingMessageAvailability(boolean available, int seq);
void onTimeShiftMode(int mode, int seq);
void onAvailableSpeeds(in float[] speeds, int seq);
- void onTvMessage(in String type, in Bundle data, int seq);
+ void onTvMessage(int type, in Bundle data, int seq);
void onTuned(in Uri channelUri, int seq);
// For the recording session
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 113c858..14c020e 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -116,10 +116,10 @@
// For ad request
void requestAd(in IBinder sessionToken, in AdRequest request, int userId);
- void notifyAdBuffer(in IBinder sessionToken, in AdBuffer buffer, int userId);
+ void notifyAdBufferReady(in IBinder sessionToken, in AdBuffer buffer, int userId);
// For TV Message
- void notifyTvMessage(in IBinder sessionToken, in String type, in Bundle data, int userId);
+ void notifyTvMessage(in IBinder sessionToken, int type, in Bundle data, int userId);
// For TV input hardware binding
List<TvInputHardwareInfo> getHardwareList();
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 165a9dd..a7bd8d3 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -75,8 +75,8 @@
// For ad request
void requestAd(in AdRequest request);
- void notifyAdBuffer(in AdBuffer buffer);
+ void notifyAdBufferReady(in AdBuffer buffer);
// For TV messages
- void notifyTvMessage(in String type, in Bundle data);
+ void notifyTvMessage(int type, in Bundle data);
}
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 449c2d6..a52e9a5 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -66,5 +66,5 @@
void onAdBufferConsumed(in AdBuffer buffer);
// For messages sent from the TV input
- void onTvMessage(in String type, in Bundle data);
+ void onTvMessage(int type, in Bundle data);
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 8389706..7946baee 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -267,7 +267,7 @@
}
case DO_SET_TV_MESSAGE_ENABLED: {
SomeArgs args = (SomeArgs) msg.obj;
- mTvInputSessionImpl.setTvMessageEnabled((String) args.arg1, (Boolean) args.arg2);
+ mTvInputSessionImpl.setTvMessageEnabled((Integer) args.arg1, (Boolean) args.arg2);
break;
}
case DO_REQUEST_AD: {
@@ -275,7 +275,7 @@
break;
}
case DO_NOTIFY_AD_BUFFER: {
- mTvInputSessionImpl.notifyAdBuffer((AdBuffer) msg.obj);
+ mTvInputSessionImpl.notifyAdBufferReady((AdBuffer) msg.obj);
break;
}
case DO_NOTIFY_TV_MESSAGE: {
@@ -465,12 +465,12 @@
}
@Override
- public void notifyAdBuffer(AdBuffer buffer) {
+ public void notifyAdBufferReady(AdBuffer buffer) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_BUFFER, buffer));
}
@Override
- public void notifyTvMessage(String type, Bundle data) {
+ public void notifyTvMessage(int type, Bundle data) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_NOTIFY_TV_MESSAGE, type, data));
}
diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java
index fb4e99c..c4fc26e 100644
--- a/media/java/android/media/tv/TableResponse.java
+++ b/media/java/android/media/tv/TableResponse.java
@@ -64,7 +64,10 @@
* @param tableUri The URI of the table in the database.
* @param version The version number of requested table.
* @param size The Size number of table in bytes.
+ *
+ * @deprecated use {@link Builder} instead.
*/
+ @Deprecated
public TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
@Nullable Uri tableUri, int version, int size) {
super(RESPONSE_TYPE, requestId, sequence, responseResult);
@@ -76,30 +79,7 @@
}
/**
- * Constructs a TableResponse with a table URI.
- *
- * @param requestId The ID is used to associate the response with the request.
- * @param sequence The sequence number which indicates the order of related responses.
- * @param responseResult The result for the response. It's one of {@link #RESPONSE_RESULT_OK},
- * {@link #RESPONSE_RESULT_CANCEL}, {@link #RESPONSE_RESULT_ERROR}.
- * @param tableByteArray The byte array which stores the table in bytes. The structure and
- * syntax of the table depends on the table name in
- * {@link TableRequest#getTableName()} and the corresponding standard.
- * @param version The version number of requested table.
- * @param size The Size number of table in bytes.
- */
- public TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
- @NonNull byte[] tableByteArray, int version, int size) {
- super(RESPONSE_TYPE, requestId, sequence, responseResult);
- mVersion = version;
- mSize = size;
- mTableUri = null;
- mTableByteArray = tableByteArray;
- mTableSharedMemory = null;
- }
-
- /**
- * Constructs a TableResponse with a table URI.
+ * Constructs a TableResponse.
*
* @param requestId The ID is used to associate the response with the request.
* @param sequence The sequence number which indicates the order of related responses.
@@ -112,17 +92,128 @@
* {@link TableRequest#getTableName()} and the corresponding standard.
* @param version The version number of requested table.
* @param size The Size number of table in bytes.
+ * @param tableUri The URI of the table in the database.
+ * @param tableByteArray The byte array which stores the table in bytes. The structure and
+ * syntax of the table depends on the table name in
+ * @param tableSharedMemory The shared memory which stores the table. The table size can be
+ * large so using a shared memory optimizes the data
+ * communication between the table data source and the receiver. The
+ * structure syntax of the table depends on the table name in
+ * {@link TableRequest#getTableName()} and the corresponding standard.
*/
- public TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
- @NonNull SharedMemory tableSharedMemory, int version, int size) {
+ private TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ int version, int size, Uri tableUri, byte[] tableByteArray,
+ SharedMemory tableSharedMemory) {
super(RESPONSE_TYPE, requestId, sequence, responseResult);
mVersion = version;
mSize = size;
- mTableUri = null;
- mTableByteArray = null;
+ mTableUri = tableUri;
+ mTableByteArray = tableByteArray;
mTableSharedMemory = tableSharedMemory;
}
+ /**
+ * Builder for {@link TableResponse}.
+ */
+ public static final class Builder {
+ private final int mRequestId;
+ private final int mSequence;
+ @ResponseResult
+ private final int mResponseResult;
+ private final int mVersion;
+ private final int mSize;
+ private Uri mTableUri;
+ private byte[] mTableByteArray;
+ private SharedMemory mTableSharedMemory;
+
+ /**
+ * Constructs a Builder object of {@link TableResponse}.
+ *
+ * @param requestId The ID is used to associate the response with the request.
+ * @param sequence The sequence number which indicates the order of related responses.
+ * @param responseResult The result for the response. It's one of
+ * {@link #RESPONSE_RESULT_OK}, {@link #RESPONSE_RESULT_CANCEL},
+ * {@link #RESPONSE_RESULT_ERROR}.
+ * @param version The version number of requested table.
+ * @param size The Size number of table in bytes.
+ */
+ public Builder(int requestId, int sequence, @ResponseResult int responseResult, int version,
+ int size) {
+ mRequestId = requestId;
+ mSequence = sequence;
+ mResponseResult = responseResult;
+ mVersion = version;
+ mSize = size;
+ }
+
+ /**
+ * Sets table URI.
+ *
+ * <p>For a single builder instance, at most one of table URI, table byte array, and table
+ * shared memory can be set. If more than one are set, only the last call takes precedence
+ * and others are reset to {@code null}.
+ *
+ * @param uri The URI of the table.
+ */
+ @NonNull
+ public Builder setTableUri(@NonNull Uri uri) {
+ mTableUri = uri;
+ mTableByteArray = null;
+ mTableSharedMemory = null;
+ return this;
+ }
+
+ /**
+ * Sets table byte array.
+ *
+ * <p>For a single builder instance, at most one of table URI, table byte array, and table
+ * shared memory can be set. If more than one are set, only the last call takes precedence
+ * and others are reset to {@code null}.
+ *
+ * @param bytes The byte array which stores the table in bytes. The structure and
+ * syntax of the table depends on the table name in
+ * {@link TableRequest#getTableName()} and the corresponding standard.
+ */
+ @NonNull
+ public Builder setTableByteArray(@NonNull byte[] bytes) {
+ mTableByteArray = bytes;
+ mTableUri = null;
+ mTableSharedMemory = null;
+ return this;
+ }
+
+
+ /**
+ * Sets table shared memory.
+ *
+ * <p>For a single builder instance, at most one of table URI, table byte array, and table
+ * shared memory can be set. If more than one are set, only the last call takes precedence
+ * and others are reset to {@code null}.
+ *
+ * @param sharedMemory The shared memory which stores the table. The table size can be
+ * large so using a shared memory optimizes the data
+ * communication between the table data source and the receiver. The
+ * structure syntax of the table depends on the table name in
+ * {@link TableRequest#getTableName()} and the corresponding standard.
+ */
+ @NonNull
+ public Builder setTableSharedMemory(@NonNull SharedMemory sharedMemory) {
+ mTableSharedMemory = sharedMemory;
+ mTableUri = null;
+ mTableByteArray = null;
+ return this;
+ }
+
+ /**
+ * Builds a {@link TableResponse} object.
+ */
+ @NonNull
+ public TableResponse build() {
+ return new TableResponse(mRequestId, mSequence, mResponseResult, mVersion, mSize,
+ mTableUri, mTableByteArray, mTableSharedMemory);
+ }
+ }
+
TableResponse(Parcel source) {
super(RESPONSE_TYPE, source);
String uriString = source.readString();
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 55a753f..7d6a8b4 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -22,7 +22,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -134,14 +133,17 @@
public @interface VideoUnavailableReason {}
/** Indicates that this TV message contains watermarking data */
- public static final String TV_MESSAGE_TYPE_WATERMARK = "Watermark";
+ public static final int TV_MESSAGE_TYPE_WATERMARK = 1;
/** Indicates that this TV message contains Closed Captioning data */
- public static final String TV_MESSAGE_TYPE_CLOSED_CAPTION = "CC";
+ public static final int TV_MESSAGE_TYPE_CLOSED_CAPTION = 2;
+
+ /** Indicates that this TV message contains other data */
+ public static final int TV_MESSAGE_TYPE_OTHER = 1000;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @StringDef({TV_MESSAGE_TYPE_WATERMARK, TV_MESSAGE_TYPE_CLOSED_CAPTION})
+ @IntDef({TV_MESSAGE_TYPE_WATERMARK, TV_MESSAGE_TYPE_CLOSED_CAPTION, TV_MESSAGE_TYPE_OTHER})
public @interface TvMessageType {}
/**
@@ -802,7 +804,7 @@
* @param type The type of message received, such as {@link #TV_MESSAGE_TYPE_WATERMARK}
* @param data The raw data of the message
*/
- public void onTvMessage(Session session, @TvInputManager.TvMessageType String type,
+ public void onTvMessage(Session session, @TvInputManager.TvMessageType int type,
Bundle data) {
}
@@ -1081,7 +1083,7 @@
});
}
- void postTvMessage(String type, Bundle data) {
+ void postTvMessage(int type, Bundle data) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -1620,7 +1622,7 @@
}
@Override
- public void onTvMessage(String type, Bundle data, int seq) {
+ public void onTvMessage(int type, Bundle data, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
@@ -3230,7 +3232,7 @@
/**
* Sends TV messages to the service for testing purposes
*/
- public void notifyTvMessage(@NonNull @TvMessageType String type, @NonNull Bundle data) {
+ public void notifyTvMessage(@NonNull @TvMessageType int type, @NonNull Bundle data) {
try {
mService.notifyTvMessage(mToken, type, data, mUserId);
} catch (RemoteException e) {
@@ -3642,13 +3644,13 @@
/**
* Notifies when the advertisement buffer is filled and ready to be read.
*/
- public void notifyAdBuffer(AdBuffer buffer) {
+ public void notifyAdBufferReady(AdBuffer buffer) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
- mService.notifyAdBuffer(mToken, buffer, mUserId);
+ mService.notifyAdBufferReady(mToken, buffer, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 9f40d70..3c6ed91 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1032,7 +1032,7 @@
* {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param data The data sent with the message.
*/
- public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+ public void notifyTvMessage(@TvInputManager.TvMessageType int type,
@NonNull Bundle data) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@@ -1351,7 +1351,7 @@
*
* @param buffer The {@link AdBuffer} that became ready for playback.
*/
- public void onAdBuffer(@NonNull AdBuffer buffer) {
+ public void onAdBufferReady(@NonNull AdBuffer buffer) {
}
/**
@@ -1486,11 +1486,12 @@
/**
* Called when the application enables or disables the detection of the specified message
* type.
- * @param type The {@link TvInputManager.TvMessageType} of message that was sent.
+ * @param type The type of message received, such as
+ * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param enabled {@code true} if TV message detection is enabled,
* {@code false} otherwise.
*/
- public void onSetTvMessageEnabled(@NonNull @TvInputManager.TvMessageType String type,
+ public void onSetTvMessageEnabled(@TvInputManager.TvMessageType int type,
boolean enabled) {
}
@@ -1856,9 +1857,9 @@
}
/**
- * Calls {@link #onSetTvMessageEnabled(String, boolean)}.
+ * Calls {@link #onSetTvMessageEnabled(int, boolean)}.
*/
- void setTvMessageEnabled(String type, boolean enabled) {
+ void setTvMessageEnabled(int type, boolean enabled) {
onSetTvMessageEnabled(type, enabled);
}
@@ -2050,8 +2051,8 @@
onRequestAd(request);
}
- void notifyAdBuffer(AdBuffer buffer) {
- onAdBuffer(buffer);
+ void notifyAdBufferReady(AdBuffer buffer) {
+ onAdBufferReady(buffer);
}
void onTvMessageReceived(String type, Bundle data) {
diff --git a/media/java/android/media/tv/TvRecordingInfo.java b/media/java/android/media/tv/TvRecordingInfo.java
index 60ceb83..59915fc 100644
--- a/media/java/android/media/tv/TvRecordingInfo.java
+++ b/media/java/android/media/tv/TvRecordingInfo.java
@@ -131,7 +131,6 @@
* cause the recording to start later than the specified time. This should cause the actual
* duration of the recording to decrease.
*/
- @NonNull
public long getStartPaddingMillis() {
return mStartPaddingMillis;
}
@@ -144,7 +143,6 @@
* cause the recording to end earlier than the specified time. This should cause the actual
* duration of the recording to decrease.
*/
- @NonNull
public long getEndPaddingMillis() {
return mEndPaddingMillis;
}
@@ -176,7 +174,6 @@
* https://www.oipf.tv/docs/OIPF-T1-R2_Specification-Volume-5-Declarative-Application-Environment-v2_3-2014-01-24.pdf
* ">Open IPTV Forum Release 2 Specification</a>. It is described in Volume 5, section 7.10.1.1.
*/
- @NonNull
@DaysOfWeek
public int getRepeatDays() {
return mRepeatDays;
@@ -228,7 +225,6 @@
* Returns the scheduled start time of the recording in milliseconds since the epoch.
*/
@IntRange(from = 0)
- @NonNull
public long getScheduledStartTimeMillis() {
return mScheduledStartTimeMillis;
}
@@ -237,7 +233,6 @@
* Returns the scheduled duration of the recording in milliseconds since the epoch.
*/
@IntRange(from = 0)
- @NonNull
public long getScheduledDurationMillis() {
return mScheduledDurationMillis;
}
@@ -292,7 +287,6 @@
* <p> Returns -1 for recordings that have not yet started.
*/
@IntRange(from = -1)
- @NonNull
public long getRecordingStartTimeMillis() {
return mRecordingStartTimeMillis;
}
@@ -306,7 +300,6 @@
* <p> Returns -1 for recordings that have not yet started.
*/
@IntRange(from = -1)
- @NonNull
public long getRecordingDurationMillis() {
return mRecordingDurationMillis;
}
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 5aeed1f..19a2e5d 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -648,7 +648,7 @@
* @hide
*/
@TestApi
- public void notifyTvMessage(@TvInputManager.TvMessageType @NonNull String type,
+ public void notifyTvMessage(@TvInputManager.TvMessageType int type,
@NonNull Bundle data) {
if (mSession != null) {
mSession.notifyTvMessage(type, data);
@@ -738,11 +738,12 @@
/**
* Enables or disables TV message detection in the stream of the bound TV input.
*
- * @param type The type of {@link android.media.tv.TvInputManager.TvMessageType}
+ * @param type The type of message received, such as
+ * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param enabled {@code true} if you want to enable TV message detection
* {@code false} otherwise.
*/
- public void setTvMessageEnabled(@NonNull @TvInputManager.TvMessageType String type,
+ public void setTvMessageEnabled(@TvInputManager.TvMessageType int type,
boolean enabled) {
}
@@ -1251,11 +1252,12 @@
* This is called when a new TV Message has been received.
*
* @param inputId The ID of the TV input bound to this view.
- * @param type The type of {@link android.media.tv.TvInputManager.TvMessageType}
+ * @param type The type of message received, such as
+ * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param data The raw data of the message
*/
public void onTvMessage(@NonNull String inputId,
- @NonNull @TvInputManager.TvMessageType String type, @NonNull Bundle data) {
+ @TvInputManager.TvMessageType int type, @NonNull Bundle data) {
}
}
@@ -1670,7 +1672,7 @@
}
@Override
- public void onTvMessage(Session session, String type, Bundle data) {
+ public void onTvMessage(Session session, int type, Bundle data) {
if (DEBUG) {
Log.d(TAG, "onTvMessage(type=" + type + ", data=" + data + ")");
}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 36954ad..7739184 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -39,7 +39,7 @@
void onSessionStateChanged(int state, int err, int seq);
void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
void onTeletextAppStateChanged(int state, int seq);
- void onAdBuffer(in AdBuffer buffer, int seq);
+ void onAdBufferReady(in AdBuffer buffer, int seq);
void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
void onTimeShiftCommandRequest(in String cmdType, in Bundle parameters, int seq);
void onSetVideoBounds(in Rect rect, int seq);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index 89847a7..41cbe4a 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -94,7 +94,7 @@
void notifyRecordingStarted(in IBinder sessionToken, in String recordingId, String requestId,
int userId);
void notifyRecordingStopped(in IBinder sessionToken, in String recordingId, int userId);
- void notifyTvMessage(in IBinder sessionToken, in String type, in Bundle data, int userId);
+ void notifyTvMessage(in IBinder sessionToken, in int type, in Bundle data, int userId);
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
index f17d1b7..052bc3d 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
@@ -72,7 +72,7 @@
void notifySignalStrength(int strength);
void notifyRecordingStarted(in String recordingId, in String requestId);
void notifyRecordingStopped(in String recordingId);
- void notifyTvMessage(in String type, in Bundle data);
+ void notifyTvMessage(int type, in Bundle data);
void setSurface(in Surface surface);
void dispatchSurfaceChanged(int format, int width, int height);
void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index 7db8604..9e43e79 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -38,7 +38,7 @@
void onSessionStateChanged(int state, int err);
void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
void onTeletextAppStateChanged(int state);
- void onAdBuffer(in AdBuffer buffer);
+ void onAdBufferReady(in AdBuffer buffer);
void onCommandRequest(in String cmdType, in Bundle parameters);
void onTimeShiftCommandRequest(in String cmdType, in Bundle parameters);
void onSetVideoBounds(in Rect rect);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index 6eed483..d3f598a 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -243,7 +243,7 @@
}
case DO_NOTIFY_TV_MESSAGE: {
SomeArgs args = (SomeArgs) msg.obj;
- mSessionImpl.notifyTvMessage((String) args.arg1, (Bundle) args.arg2);
+ mSessionImpl.notifyTvMessage((Integer) args.arg1, (Bundle) args.arg2);
args.recycle();
break;
}
@@ -520,7 +520,7 @@
}
@Override
- public void notifyTvMessage(String type, Bundle data) {
+ public void notifyTvMessage(int type, Bundle data) {
mCaller.executeOrSendMessage(
mCaller.obtainMessageOO(DO_NOTIFY_TV_MESSAGE, type, data));
}
@@ -558,7 +558,7 @@
@Override
public void notifyRecordingStarted(String recordingId, String requestId) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(
- DO_NOTIFY_RECORDING_STARTED, recordingId, recordingId));
+ DO_NOTIFY_RECORDING_STARTED, recordingId, requestId));
}
@Override
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 3e31bce3..fc8fe5c 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -681,14 +681,14 @@
}
@Override
- public void onAdBuffer(AdBuffer buffer, int seq) {
+ public void onAdBufferReady(AdBuffer buffer, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
Log.e(TAG, "Callback not found for seq " + seq);
return;
}
- record.postAdBuffer(buffer);
+ record.postAdBufferReady(buffer);
}
}
};
@@ -1751,7 +1751,7 @@
/**
* Notifies Interactive APP session when a new TV message is received.
*/
- public void notifyTvMessage(String type, Bundle data) {
+ public void notifyTvMessage(int type, Bundle data) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
@@ -2245,12 +2245,12 @@
});
}
- void postAdBuffer(AdBuffer buffer) {
+ void postAdBufferReady(AdBuffer buffer) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mSession.getInputSession() != null) {
- mSession.getInputSession().notifyAdBuffer(buffer);
+ mSession.getInputSession().notifyAdBufferReady(buffer);
}
}
});
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 1ae82f4..7dfe16a 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -38,6 +38,7 @@
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvRecordingInfo;
@@ -630,6 +631,8 @@
* {@link #requestStartRecording(String, Uri)} is called.
* {@code null} if the recording is not triggered by a
* {@link #requestStartRecording(String, Uri)} request.
+ * This ID should be created by the {@link TvInteractiveAppService} and
+ * can be any string.
* @see #onRecordingStopped(String)
*/
public void onRecordingStarted(@NonNull String recordingId, @Nullable String requestId) {
@@ -708,6 +711,8 @@
* @param requestId The ID of the request when
* {@link #requestScheduleRecording} is called.
* {@code null} if the recording is not triggered by a request.
+ * This ID should be created by the {@link TvInteractiveAppService} and
+ * can be any string.
*/
public void onRecordingScheduled(@NonNull String recordingId, @Nullable String requestId) {
}
@@ -916,7 +921,7 @@
* {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param data The raw data of the message
*/
- public void onTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+ public void onTvMessage(@TvInputManager.TvMessageType int type,
@NonNull Bundle data) {
}
@@ -1338,8 +1343,12 @@
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
* {@link #onRecordingStarted(String, String)} for this request is the
- * same as the ID sent here.
- * @param programUri The URI for the TV program to record.
+ * same as the ID sent here. This should be defined by the
+ * {@link TvInteractiveAppService} and can be any string.
+ * Should this API be called with the same requestId twice, both
+ * requests should be handled regardless by the TV application.
+ * @param programUri The URI for the TV program to record, built by
+ * {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
* @see android.media.tv.TvRecordingClient#startRecording(Uri)
*/
@CallSuper
@@ -1390,7 +1399,10 @@
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
* {@link #onRecordingScheduled(String, String)} for this request is the
- * same as the ID sent here.
+ * same as the ID sent here. This should be defined by the
+ * {@link TvInteractiveAppService} and can be any string.
+ * Should this API be called with the same requestId twice, both requests
+ * should be handled regardless by the TV application.
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel to be recorded.
* @param programUri The URI of the TV program to be recorded.
@@ -1424,7 +1436,10 @@
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
* {@link #onRecordingScheduled(String, String)} for this request is the
- * same as the ID sent here.
+ * same as the ID sent here. This should be defined by the
+ * {@link TvInteractiveAppService} and can be any string. Should this API
+ * be called with the same requestId twice, both requests should be handled
+ * regardless by the TV application.
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel to be recorded.
* @param startTime The start time of the recording in milliseconds since epoch.
@@ -1507,8 +1522,7 @@
* @param type The type of recording to retrieve.
*/
@CallSuper
- public void requestTvRecordingInfoList(@NonNull @TvRecordingInfo.TvRecordingListType
- int type) {
+ public void requestTvRecordingInfoList(@TvRecordingInfo.TvRecordingListType int type) {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
@@ -1753,7 +1767,7 @@
onAdResponse(response);
}
- void notifyTvMessage(String type, Bundle data) {
+ void notifyTvMessage(int type, Bundle data) {
if (DEBUG) {
Log.d(TAG, "notifyTvMessage (type=" + type + ", data= " + data + ")");
}
@@ -1942,7 +1956,7 @@
* @param buffer The {@link AdBuffer} to be received
*/
@CallSuper
- public void notifyAdBuffer(@NonNull AdBuffer buffer) {
+ public void notifyAdBufferReady(@NonNull AdBuffer buffer) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
@@ -1950,10 +1964,10 @@
try {
if (DEBUG) {
Log.d(TAG,
- "notifyAdBuffer(buffer=" + buffer + ")");
+ "notifyAdBufferReady(buffer=" + buffer + ")");
}
if (mSessionCallback != null) {
- mSessionCallback.onAdBuffer(buffer);
+ mSessionCallback.onAdBufferReady(buffer);
}
} catch (RemoteException e) {
Log.w(TAG, "error in notifyAdBuffer", e);
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 0a8de12..1a0319b 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -675,6 +675,8 @@
* @param requestId The ID of the request when
* {@link TvInteractiveAppService.Session#requestStartRecording(String, Uri)}
* is called. {@code null} if the recording is not triggered by a request.
+ * This ID should be created by the {@link TvInteractiveAppService} and
+ * can be any string.
* @see TvInteractiveAppView#notifyRecordingStopped(String)
*/
public void notifyRecordingStarted(@NonNull String recordingId, @Nullable String requestId) {
@@ -682,7 +684,7 @@
Log.d(TAG, "notifyRecordingStarted");
}
if (mSession != null) {
- mSession.notifyRecordingStarted(recordingId, recordingId);
+ mSession.notifyRecordingStarted(recordingId, requestId);
}
}
@@ -922,6 +924,8 @@
* @param requestId The ID of the request when
* {@link TvInteractiveAppService.Session#requestScheduleRecording} is called.
* {@code null} if the recording is not triggered by a request.
+ * This ID should be created by the {@link TvInteractiveAppService} and
+ * can be any string.
*/
public void notifyRecordingScheduled(
@NonNull String recordingId, @Nullable String requestId) {
@@ -942,7 +946,7 @@
* {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
* @param data The raw data of the message
*/
- public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+ public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType int type,
@NonNull Bundle data) {
if (DEBUG) {
Log.d(TAG, "notifyTvMessage type=" + type
@@ -1214,14 +1218,17 @@
}
/**
- * This is called when {@link TvInteractiveAppService.Session#requestStartRecording(Uri)}
- * is called.
+ * This is called when
+ * {@link TvInteractiveAppService.Session#requestStartRecording(String, Uri)} is called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
- * {@link #notifyRecordingStarted(String, String)}
- * for this request should be the same as the ID received here.
+ * {@link #notifyRecordingStarted(String, String)} for this request is the
+ * same as the ID sent here. This should be defined by the
+ * TIAS and can be any string. Should this API be called with the
+ * same requestId twice, both requests should be handled regardless
+ * by the TV application.
* @param programUri The URI of the program to record
*
*/
@@ -1252,8 +1259,11 @@
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
- * {@link #notifyRecordingScheduled(String, String)} for this request
- * should be the same as the ID received here.
+ * {@link #notifyRecordingScheduled(String, String)} for this request is
+ * the same as the ID sent here. This should be defined by the
+ * TIAS and can be any string. Should this API be called with the
+ * same requestId twice, both requests should be handled regardless
+ * by the TV application.
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel to be recorded.
* @param programUri The URI of the TV program to be recorded.
@@ -1276,8 +1286,11 @@
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
- * {@link #notifyRecordingScheduled(String, String)} for this request
- * should be the same as the ID received here.
+ * {@link #notifyRecordingScheduled(String, String)} for this request is
+ * the same as the ID sent here. This should be defined by the
+ * TIAS and can be any string. Should this API be called with the
+ * same requestId twice, both requests should be handled regardless
+ * by the TV application.
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel to be recorded.
* @param startTime The start time of the recording in milliseconds since epoch.
@@ -1350,7 +1363,7 @@
*/
public void onRequestTvRecordingInfoList(
@NonNull String iAppServiceId,
- @NonNull @TvRecordingInfo.TvRecordingListType int type) {
+ @TvRecordingInfo.TvRecordingListType int type) {
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 16827da..6fd579b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -446,6 +446,7 @@
BodyMediumText(text = stringResource(
R.string.use_provider_for_all_description, entryInfo.userProviderDisplayName))
}
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
item {
CtaButtonRow(
leftButton = {
@@ -559,7 +560,11 @@
modifier = Modifier.padding(vertical = 16.dp)
)
}
- item { BodySmallText(text = createOptionInfo.footerDescription) }
+ item {
+ Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+ BodySmallText(text = createOptionInfo.footerDescription)
+ }
+ }
}
}
onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_CREATION_OPTION_SELECTION)
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
index 643af75..2071489 100644
--- a/packages/SettingsLib/Spa/build.gradle
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -24,7 +24,7 @@
}
}
plugins {
- id 'com.android.application' version '8.0.0-beta01' apply false
- id 'com.android.library' version '8.0.0-beta01' apply false
+ id 'com.android.application' version '8.0.0-beta03' apply false
+ id 'com.android.library' version '8.0.0-beta03' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
}
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 53b24b0..c3d5431 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,7 +16,7 @@
#Thu Jul 14 10:36:06 CST 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index db6cc1a..7f3b0ff 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -16,7 +16,6 @@
package com.android.providers.settings;
-import android.annotation.NonNull;
import android.os.Bundle;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -60,10 +59,6 @@
// Maximum size of an individual backing store
static final int MAX_BACKING_STORE_SIZE = MemoryIntArray.getMaxSize();
- // Use an empty string to track the generation number of all non-predefined, unset settings
- // The generation number is only increased when a new non-predefined setting is inserted
- private static final String DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS = "";
-
public GenerationRegistry(Object lock) {
mLock = lock;
}
@@ -77,10 +72,6 @@
(SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG);
// Only store the prefix if the mutated setting is a config
final String indexMapKey = isConfig ? (name.split("/")[0] + "/") : name;
- incrementGenerationInternal(key, indexMapKey);
- }
-
- private void incrementGenerationInternal(int key, @NonNull String indexMapKey) {
synchronized (mLock) {
final MemoryIntArray backingStore = getBackingStoreLocked(key,
/* createIfNotExist= */ false);
@@ -96,8 +87,7 @@
final int generation = backingStore.get(index) + 1;
backingStore.set(index, generation);
if (DEBUG) {
- Slog.i(LOG_TAG, "Incremented generation for "
- + (indexMapKey.isEmpty() ? "unset settings" : "setting:" + indexMapKey)
+ Slog.i(LOG_TAG, "Incremented generation for setting:" + indexMapKey
+ " key:" + SettingsState.keyToString(key)
+ " at index:" + index);
}
@@ -108,18 +98,6 @@
}
}
- // A new, non-predefined setting has been inserted, increment the tracking number for all unset
- // settings
- public void incrementGenerationForUnsetSettings(int key) {
- final boolean isConfig =
- (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG);
- if (isConfig) {
- // No need to track new settings for configs
- return;
- }
- incrementGenerationInternal(key, DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS);
- }
-
/**
* Return the backing store's reference, the index and the current generation number
* of a cached setting. If it was not in the backing store, first create the entry in it before
@@ -146,8 +124,8 @@
bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
backingStore.get(index));
if (DEBUG) {
- Slog.i(LOG_TAG, "Exported index:" + index + " for "
- + (indexMapKey.isEmpty() ? "unset settings" : "setting:" + indexMapKey)
+ Slog.i(LOG_TAG, "Exported index:" + index
+ + " for setting:" + indexMapKey
+ " key:" + SettingsState.keyToString(key));
}
} catch (IOException e) {
@@ -157,10 +135,6 @@
}
}
- public void addGenerationDataForUnsetSettings(Bundle bundle, int key) {
- addGenerationData(bundle, key, /* indexMapKey= */ DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS);
- }
-
public void onUserRemoved(int userId) {
final int secureKey = SettingsState.makeKey(
SettingsState.SETTINGS_TYPE_SECURE, userId);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 27c8cdf..832c1b9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1173,7 +1173,7 @@
Slog.v(LOG_TAG, "setAllConfigSettings for prefix: " + prefix);
}
- enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+ enforceDeviceConfigWritePermission(getContext(), keyValues.keySet());
final String callingPackage = resolveCallingPackage();
synchronized (mLock) {
@@ -1192,7 +1192,8 @@
Slog.v(LOG_TAG, "setSyncDisabledModeConfig(" + syncDisabledMode + ")");
}
- enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+ enforceHasAtLeastOnePermission(Manifest.permission.WRITE_DEVICE_CONFIG,
+ Manifest.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG);
synchronized (mLock) {
setSyncDisabledModeConfigLocked(syncDisabledMode);
@@ -1204,7 +1205,8 @@
Slog.v(LOG_TAG, "getSyncDisabledModeConfig");
}
- enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+ enforceHasAtLeastOnePermission(Manifest.permission.WRITE_DEVICE_CONFIG,
+ Manifest.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG);
synchronized (mLock) {
return getSyncDisabledModeConfigLocked();
@@ -1289,13 +1291,13 @@
private boolean mutateConfigSetting(String name, String value, String prefix,
boolean makeDefault, int operation, int mode) {
- enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
final String callingPackage = resolveCallingPackage();
// Perform the mutation.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
+ enforceDeviceConfigWritePermission(getContext(), Collections.singleton(name));
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
callingPackage, false, null,
@@ -1303,11 +1305,14 @@
}
case MUTATION_OPERATION_DELETE: {
+ enforceDeviceConfigWritePermission(getContext(), Collections.singleton(name));
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, name, false, null);
}
case MUTATION_OPERATION_RESET: {
+ enforceDeviceConfigWritePermission(getContext(),
+ getAllConfigFlags(prefix).keySet());
mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, callingPackage, mode, null, prefix);
} return true;
@@ -1464,7 +1469,7 @@
boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
int mode, boolean overrideableByRestore) {
// Make sure the caller can change the settings - treated as secure.
- enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
+ enforceHasAtLeastOnePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
@@ -1752,7 +1757,7 @@
boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
int mode, boolean overrideableByRestore) {
// Make sure the caller can change the settings.
- enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
+ enforceHasAtLeastOnePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
@@ -2277,11 +2282,57 @@
}
}
- private void enforceWritePermission(String permission) {
- if (getContext().checkCallingOrSelfPermission(permission)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Permission denial: writing to settings requires:"
- + permission);
+ private void enforceHasAtLeastOnePermission(String ...permissions) {
+ for (String permission : permissions) {
+ if (getContext().checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+ throw new SecurityException("Permission denial, must have one of: "
+ + Arrays.toString(permissions));
+ }
+
+ /**
+ * Throws an exception if write permissions are not granted for {@code flags}.
+ * <p>
+ * Write permissions are granted if the calling UID is root, or the
+ * WRITE_DEVICE_CONFIG permission is granted, or the WRITE_DEVICE_CONFIG_ALLOWLIST
+ * permission is granted and each flag in {@code flags} is allowlisted in {@code
+ * WRITABLE_FLAG_ALLOWLIST_FLAG}.
+ *
+ * @param context the {@link Context} this is called in
+ * @param flags a list of flags to check, each one of the form 'namespace/flagName'
+ *
+ * @throws SecurityException if the above criteria are not met.
+ * @hide
+ */
+ private void enforceDeviceConfigWritePermission(
+ @NonNull Context context,
+ @NonNull Set<String> flags) {
+ boolean hasAllowlistPermission =
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG)
+ == PackageManager.PERMISSION_GRANTED;
+ boolean hasWritePermission =
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.WRITE_DEVICE_CONFIG)
+ == PackageManager.PERMISSION_GRANTED;
+ boolean isRoot = Binder.getCallingUid() == Process.ROOT_UID;
+
+ if (isRoot || hasWritePermission) {
+ return;
+ } else if (hasAllowlistPermission) {
+ for (String flag : flags) {
+ if (!DeviceConfig.getAdbWritableFlags().contains(flag)) {
+ throw new SecurityException("Permission denial for flag '"
+ + flag
+ + "'; allowlist permission granted, but must add flag to the allowlist.");
+ }
+ }
+ } else {
+ throw new SecurityException("Permission denial to mutate flag, must have root, "
+ + "WRITE_DEVICE_CONFIG, or ALLOWLISTED_WRITE_DEVICE_CONFIG");
}
}
@@ -2327,15 +2378,11 @@
result.putString(Settings.NameValueTable.VALUE,
(setting != null && !setting.isNull()) ? setting.getValue() : null);
- synchronized (mLock) {
- if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) {
- // Individual generation tracking for predefined settings even if they are unset
+ if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) {
+ // Don't track generation for non-existent settings unless the name is predefined
+ synchronized (mLock) {
mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
SettingsState.makeKey(type, userId), name);
- } else {
- // All non-predefined, unset settings are tracked using the same generation number
- mSettingsRegistry.mGenerationRegistry.addGenerationDataForUnsetSettings(result,
- SettingsState.makeKey(type, userId));
}
}
return result;
@@ -2349,8 +2396,7 @@
} else if (type == SETTINGS_TYPE_SYSTEM) {
return sAllSystemSettings.contains(name);
} else {
- // Consider all config settings predefined because they are used by system apps only
- return type == SETTINGS_TYPE_CONFIG;
+ return false;
}
}
@@ -2359,13 +2405,14 @@
Bundle result = new Bundle();
result.putSerializable(Settings.NameValueTable.VALUE, keyValues);
if (trackingGeneration) {
+ // Track generation even if the namespace is empty because this is for system apps
synchronized (mLock) {
- // Track generation even if namespace is empty because this is for system apps only
mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
- SettingsState.makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM),
- prefix);
+ mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_CONFIG,
+ UserHandle.USER_SYSTEM).mKey, prefix);
}
}
+
return result;
}
@@ -3056,15 +3103,10 @@
final int key = makeKey(type, userId);
boolean success = false;
- boolean wasUnsetNonPredefinedSetting = false;
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState != null) {
- if (!isSettingPreDefined(name, type) && !settingsState.hasSetting(name)) {
- wasUnsetNonPredefinedSetting = true;
- }
success = settingsState.insertSettingLocked(name, value,
- tag, makeDefault, forceNonSystemPackage, packageName,
- overrideableByRestore);
+ tag, makeDefault, forceNonSystemPackage, packageName, overrideableByRestore);
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
@@ -3073,11 +3115,6 @@
if (forceNotify || success) {
notifyForSettingsChange(key, name);
- if (wasUnsetNonPredefinedSetting) {
- // Increment the generation number for all non-predefined, unset settings,
- // because a new non-predefined setting has been inserted
- mGenerationRegistry.incrementGenerationForUnsetSettings(key);
- }
}
if (success) {
logSettingChanged(userId, name, type, CHANGE_TYPE_INSERT);
@@ -3709,7 +3746,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 214;
+ private static final int SETTINGS_VERSION = 215;
private final int mUserId;
@@ -5664,6 +5701,49 @@
currentVersion = 214;
}
+ if (currentVersion == 214) {
+ // Version 214: Set a default value for Credential Manager service.
+
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting currentSetting = secureSettings
+ .getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE);
+ if (currentSetting.isNull()) {
+ final int resourceId =
+ com.android.internal.R.string.config_defaultCredentialProviderService;
+ final Resources resources = getContext().getResources();
+ // If the config has not be defined we might get an exception. We also get
+ // values from both the string array type and the single string in case the
+ // OEM uses the wrong one.
+ final List<String> providers = new ArrayList<>();
+ try {
+ providers.addAll(Arrays.asList(resources.getStringArray(resourceId)));
+ } catch (Resources.NotFoundException e) {
+ Slog.w(LOG_TAG,
+ "Get default array Cred Provider not found: " + e.toString());
+ }
+ try {
+ final String storedValue = resources.getString(resourceId);
+ if (!TextUtils.isEmpty(storedValue)) {
+ providers.add(storedValue);
+ }
+ } catch (Resources.NotFoundException e) {
+ Slog.w(LOG_TAG,
+ "Get default Cred Provider not found: " + e.toString());
+ }
+
+ if (!providers.isEmpty()) {
+ final String defaultValue = String.join(":", providers);
+ Slog.d(LOG_TAG, "Setting [" + defaultValue + "] as CredMan Service "
+ + "for user " + userId);
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.Secure.CREDENTIAL_SERVICE, defaultValue, null, true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+
+ currentVersion = 215;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4d8705f..c388826 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -759,12 +759,6 @@
mPackageToMemoryUsage.put(packageName, newSize);
}
- public boolean hasSetting(String name) {
- synchronized (mLock) {
- return hasSettingLocked(name);
- }
- }
-
@GuardedBy("mLock")
private boolean hasSettingLocked(String name) {
return mSettings.indexOfKey(name) >= 0;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
index 6ec8146..d34fe694 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
@@ -151,26 +151,6 @@
checkBundle(b, 0, 1, false);
}
- @Test
- public void testUnsetSettings() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
- final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
- final String testSecureSetting = "test_secure_setting";
- Bundle b = new Bundle();
- generationRegistry.addGenerationData(b, secureKey, testSecureSetting);
- checkBundle(b, 0, 1, false);
- generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
- checkBundle(b, 1, 1, false);
- generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
- // Test that unset settings always have the same index
- checkBundle(b, 1, 1, false);
- generationRegistry.incrementGenerationForUnsetSettings(secureKey);
- // Test that the generation number of the unset settings have increased
- generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
- checkBundle(b, 1, 2, false);
- }
-
-
private void checkBundle(Bundle b, int expectedIndex, int expectedGeneration, boolean isNull)
throws IOException {
final MemoryIntArray array = getArray(b);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d02e569..8cbf5f8 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -148,6 +148,8 @@
<uses-permission android:name="android.permission.LOCATION_BYPASS" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG" />
<uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6176c61..dc24dce 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -967,6 +967,7 @@
android:enabled="false"
android:exported="true"
android:excludeFromRecents="true"
+ android:resizeableActivity="false"
android:theme="@android:style/Theme.NoDisplay"
android:label="@string/note_task_button_label"
android:icon="@drawable/ic_note_task_shortcut_widget">
@@ -981,6 +982,7 @@
android:name=".notetask.shortcut.LaunchNoteTaskActivity"
android:exported="true"
android:excludeFromRecents="true"
+ android:resizeableActivity="false"
android:theme="@android:style/Theme.NoDisplay" />
<!-- endregion -->
diff --git a/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml b/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml
index 92c9527..401ac73 100644
--- a/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml
+++ b/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
+ android:width="32dp"
+ android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal">
diff --git a/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml b/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml
index 209681f..9a850e8 100644
--- a/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml
+++ b/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
+ android:width="32dp"
+ android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal">
diff --git a/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml b/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml
index a3dc816..54322c5 100644
--- a/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml
+++ b/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
+ android:width="32dp"
+ android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal">
diff --git a/packages/SystemUI/res/drawable/ic_open_in_full.xml b/packages/SystemUI/res/drawable/ic_open_in_full.xml
index c7f3236..7081c4b 100644
--- a/packages/SystemUI/res/drawable/ic_open_in_full.xml
+++ b/packages/SystemUI/res/drawable/ic_open_in_full.xml
@@ -14,8 +14,8 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
+ android:width="24dp"
+ android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal">
diff --git a/packages/SystemUI/res/layout/window_magnification_settings_view.xml b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
index ae0f8f4..3d0741c 100644
--- a/packages/SystemUI/res/layout/window_magnification_settings_view.xml
+++ b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
@@ -35,7 +35,7 @@
android:text="@string/accessibility_magnifier_size"
android:textAppearance="@style/TextAppearance.MagnificationSetting.Title"
android:focusable="true"
- android:layout_gravity="center_vertical|left" />
+ android:layout_gravity="center_vertical" />
<Button
android:id="@+id/magnifier_edit_button"
@@ -45,25 +45,22 @@
android:text="@string/accessibility_magnifier_edit"
android:textAppearance="@style/TextAppearance.MagnificationSetting.EditButton"
android:focusable="true"
- android:layout_gravity="right" />
+ android:layout_gravity="center_vertical" />
</LinearLayout>
<LinearLayout
android:background="@drawable/accessibility_magnification_setting_view_image_btn_layout_bg"
- android:layout_width="@dimen/magnification_setting_image_button_background_width"
+ android:layout_width="match_parent"
android:layout_height="@dimen/magnification_setting_image_button_height"
+ android:minWidth="@dimen/magnification_setting_image_button_background_width"
android:orientation="horizontal">
<ImageButton
android:id="@+id/magnifier_small_button"
android:layout_width="0dp"
android:layout_height="@dimen/magnification_setting_image_button_height"
android:layout_weight="1"
- android:scaleType="fitCenter"
+ android:scaleType="center"
android:background="@drawable/accessibility_magnification_setting_view_image_btn_bg"
- android:paddingLeft="@dimen/magnification_setting_image_button_padding_horizontal"
- android:paddingRight="@dimen/magnification_setting_image_button_padding_horizontal"
- android:paddingTop="@dimen/magnification_setting_image_button_padding_vertical"
- android:paddingBottom="@dimen/magnification_setting_image_button_padding_vertical"
android:src="@drawable/ic_magnification_menu_small"
android:tint="@color/accessibility_magnification_image_button_tint"
android:tintMode="src_atop" />
@@ -73,12 +70,8 @@
android:layout_width="0dp"
android:layout_height="@dimen/magnification_setting_image_button_height"
android:layout_weight="1"
- android:scaleType="fitCenter"
+ android:scaleType="center"
android:background="@drawable/accessibility_magnification_setting_view_image_btn_bg"
- android:paddingLeft="@dimen/magnification_setting_image_button_padding_horizontal"
- android:paddingRight="@dimen/magnification_setting_image_button_padding_horizontal"
- android:paddingTop="@dimen/magnification_setting_image_button_padding_vertical"
- android:paddingBottom="@dimen/magnification_setting_image_button_padding_vertical"
android:src="@drawable/ic_magnification_menu_medium"
android:tint="@color/accessibility_magnification_image_button_tint"
android:tintMode="src_atop" />
@@ -88,12 +81,8 @@
android:layout_width="0dp"
android:layout_height="@dimen/magnification_setting_image_button_height"
android:layout_weight="1"
- android:scaleType="fitCenter"
+ android:scaleType="center"
android:background="@drawable/accessibility_magnification_setting_view_image_btn_bg"
- android:paddingLeft="@dimen/magnification_setting_image_button_padding_horizontal"
- android:paddingRight="@dimen/magnification_setting_image_button_padding_horizontal"
- android:paddingTop="@dimen/magnification_setting_image_button_padding_vertical"
- android:paddingBottom="@dimen/magnification_setting_image_button_padding_vertical"
android:src="@drawable/ic_magnification_menu_large"
android:tint="@color/accessibility_magnification_image_button_tint"
android:tintMode="src_atop" />
@@ -103,16 +92,8 @@
android:layout_width="0dp"
android:layout_height="@dimen/magnification_setting_image_button_height"
android:layout_weight="1"
- android:scaleType="fitCenter"
+ android:scaleType="center"
android:background="@drawable/accessibility_magnification_setting_view_image_btn_bg"
- android:paddingLeft
- ="@dimen/magnification_setting_image_button_open_in_full_padding_horizontal"
- android:paddingRight
- ="@dimen/magnification_setting_image_button_open_in_full_padding_horizontal"
- android:paddingTop
- ="@dimen/magnification_setting_image_button_open_in_full_padding_vertical"
- android:paddingBottom
- ="@dimen/magnification_setting_image_button_open_in_full_padding_vertical"
android:src="@drawable/ic_open_in_full"
android:tint="@color/accessibility_magnification_image_button_tint"
android:tintMode="src_atop" />
@@ -130,7 +111,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:singleLine="true"
android:text="@string/accessibility_allow_diagonal_scrolling"
android:textAppearance="@style/TextAppearance.MagnificationSetting.Title"
android:layout_gravity="center_vertical" />
@@ -166,16 +146,11 @@
<Button
android:id="@+id/magnifier_done_button"
android:background="@drawable/accessibility_window_magnification_button_done_bg"
- android:minHeight="@dimen/magnification_setting_button_done_height"
- android:layout_width="@dimen/magnification_setting_button_done_width"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/accessibility_magnification_done"
android:textAppearance="@style/TextAppearance.MagnificationSetting.DoneButton"
android:focusable="true"
android:layout_gravity="center_horizontal"
- android:layout_marginTop="@dimen/magnification_setting_view_margin"
- android:paddingLeft="@dimen/magnification_setting_button_done_padding_horizontal"
- android:paddingRight="@dimen/magnification_setting_button_done_padding_horizontal"
- android:paddingTop="@dimen/magnification_setting_button_done_padding_vertical"
- android:paddingBottom="@dimen/magnification_setting_button_done_padding_vertical" />
+ android:layout_marginTop="@dimen/magnification_setting_view_margin"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2cd2173..6596ed2 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1358,6 +1358,7 @@
<item name="android:fontFamily">google-sans</item>
<item name="android:textColor">?androidprv:attr/textColorPrimary</item>
<item name="android:textSize">@dimen/magnification_setting_text_size</item>
+ <item name="android:singleLine">true</item>
</style>
<style name="TextAppearance.MagnificationSetting.EditButton">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index 8690b36..0c2341f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -33,8 +33,6 @@
*/
public static final int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
- // Contains the portion of the thumbnail that is unclipped when fullscreen progress = 1.
- private final RectF mClippedInsets = new RectF();
private final Matrix mMatrix = new Matrix();
private boolean mIsOrientationChanged;
private SplitBounds mSplitBounds;
@@ -70,33 +68,6 @@
int thumbnailRotation = thumbnailData.rotation;
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
RectF thumbnailClipHint = new RectF();
-
- float scaledTaskbarSize;
- float canvasScreenRatio;
- if (mSplitBounds != null) {
- if (mSplitBounds.appsStackedVertically) {
- if (mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
- // Top app isn't cropped at all by taskbar
- canvasScreenRatio = 0;
- } else {
- // Same as fullscreen ratio
- canvasScreenRatio = (float) canvasWidth / screenWidthPx;
- }
- } else {
- // For landscape, scale the width
- float taskPercent = mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? mSplitBounds.leftTaskPercent
- : (1 - (mSplitBounds.leftTaskPercent + mSplitBounds.dividerWidthPercent));
- // Scale landscape width to that of actual screen
- float fullscreenTaskWidth = screenWidthPx * taskPercent;
- canvasScreenRatio = canvasWidth / fullscreenTaskWidth;
- }
- } else {
- canvasScreenRatio = (float) canvasWidth / screenWidthPx;
- }
- scaledTaskbarSize = taskbarSize * canvasScreenRatio;
- thumbnailClipHint.bottom = isLargeScreen ? scaledTaskbarSize : 0;
-
float scale = thumbnailData.scale;
final float thumbnailScale;
@@ -116,10 +87,8 @@
float surfaceWidth = thumbnailBounds.width() / scale;
float surfaceHeight = thumbnailBounds.height() / scale;
- float availableWidth = surfaceWidth
- - (thumbnailClipHint.left + thumbnailClipHint.right);
- float availableHeight = surfaceHeight
- - (thumbnailClipHint.top + thumbnailClipHint.bottom);
+ float availableWidth = surfaceWidth;
+ float availableHeight = surfaceHeight;
float canvasAspect = canvasWidth / (float) canvasHeight;
float availableAspect = isRotated
@@ -210,8 +179,6 @@
setThumbnailRotation(deltaRotate, thumbnailBounds);
}
- mClippedInsets.set(0, 0, 0, scaledTaskbarSize);
-
mMatrix.postScale(thumbnailScale, thumbnailScale);
mIsOrientationChanged = isOrientationDifferent;
}
@@ -250,8 +217,4 @@
}
mMatrix.postTranslate(translateX, translateY);
}
-
- public RectF getClippedInsets() {
- return mClippedInsets;
- }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index dd60647..0d36d04 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -116,6 +116,27 @@
public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26;
// Device dreaming state
public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27;
+ // Whether the screen is currently on. Note that the screen is considered on while turning on,
+ // but not while turning off.
+ public static final int SYSUI_STATE_SCREEN_ON = 1 << 28;
+ // Whether the screen is currently transitioning into the state indicated by
+ // SYSUI_STATE_SCREEN_ON.
+ public static final int SYSUI_STATE_SCREEN_TRANSITION = 1 << 29;
+
+ // Mask for SystemUiStateFlags to isolate SYSUI_STATE_SCREEN_ON and
+ // SYSUI_STATE_SCREEN_TRANSITION, to match SCREEN_STATE_*
+ public static final int SYSUI_STATE_SCREEN_STATE_MASK =
+ SYSUI_STATE_SCREEN_ON | SYSUI_STATE_SCREEN_TRANSITION;
+ // Screen is off.
+ public static final int SCREEN_STATE_OFF = 0;
+ // Screen is on.
+ public static final int SCREEN_STATE_ON = SYSUI_STATE_SCREEN_ON;
+ // Screen is still on, but transitioning to turn off.
+ public static final int SCREEN_STATE_TURNING_OFF = SYSUI_STATE_SCREEN_TRANSITION;
+ // Screen was off and is now turning on.
+ public static final int SCREEN_STATE_TURNING_ON =
+ SYSUI_STATE_SCREEN_TRANSITION | SYSUI_STATE_SCREEN_ON;
+
// Whether the back gesture is allowed (or ignored) by the Shade
public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean(
"persist.wm.debug.shade_allow_back_gesture", false);
@@ -148,7 +169,9 @@
SYSUI_STATE_IMMERSIVE_MODE,
SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING,
SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE,
- SYSUI_STATE_DEVICE_DREAMING
+ SYSUI_STATE_DEVICE_DREAMING,
+ SYSUI_STATE_SCREEN_ON,
+ SYSUI_STATE_SCREEN_TRANSITION,
})
public @interface SystemUiStateFlags {}
@@ -187,6 +210,9 @@
str.add((flags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0
? "freeform_active_in_desktop_mode" : "");
str.add((flags & SYSUI_STATE_DEVICE_DREAMING) != 0 ? "device_dreaming" : "");
+ str.add("screen_"
+ + ((flags & SYSUI_STATE_SCREEN_TRANSITION) != 0 ? "turning_" : "")
+ + ((flags & SYSUI_STATE_SCREEN_ON) != 0 ? "on" : "off"));
return str.toString();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 64fe645..aaf6307 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2459,8 +2459,10 @@
Boolean isFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
&& mBiometricEnabledForUser.get(userId)
&& mAuthController.isFaceAuthEnrolled(userId);
+ if (mIsFaceEnrolled != isFaceEnrolled) {
+ mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
+ }
mIsFaceEnrolled = isFaceEnrolled;
- mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
}
public boolean isFaceSupported() {
@@ -3132,13 +3134,14 @@
@VisibleForTesting
boolean isUnlockWithFingerprintPossible(int userId) {
// TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
- boolean fpEnrolled = mFpm != null
+ boolean newFpEnrolled = mFpm != null
&& !mFingerprintSensorProperties.isEmpty()
&& !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
- mLogger.logFpEnrolledUpdated(userId,
- mIsUnlockWithFingerprintPossible.getOrDefault(userId, false),
- fpEnrolled);
- mIsUnlockWithFingerprintPossible.put(userId, fpEnrolled);
+ Boolean oldFpEnrolled = mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
+ if (oldFpEnrolled != newFpEnrolled) {
+ mLogger.logFpEnrolledUpdated(userId, oldFpEnrolled, newFpEnrolled);
+ }
+ mIsUnlockWithFingerprintPossible.put(userId, newFpEnrolled);
return mIsUnlockWithFingerprintPossible.get(userId);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index bfd99d3..1ae380e 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -51,6 +51,7 @@
import androidx.annotation.VisibleForTesting;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import com.android.settingslib.udfps.UdfpsOverlayParams;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
@@ -778,7 +779,7 @@
}
@Override
- public void onUdfpsLocationChanged() {
+ public void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {
updateUdfpsConfig();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index d811d30..57c9918 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -403,15 +403,21 @@
mAnimationController.deleteWindowMagnification(animationCallback);
}
+ void deleteWindowMagnification() {
+ deleteWindowMagnification(/* closeSettingPanel= */ true);
+ }
+
/**
* Deletes the magnification window.
*/
- void deleteWindowMagnification() {
+ void deleteWindowMagnification(boolean closeSettingPanel) {
if (!isWindowVisible()) {
return;
}
- closeMagnificationSettings();
+ if (closeSettingPanel) {
+ closeMagnificationSettings();
+ }
if (mMirrorSurface != null) {
mTransaction.remove(mMirrorSurface).apply();
@@ -489,7 +495,7 @@
// Recreate the window again to correct the window appearance due to density or
// window size changed not caused by rotation.
if (isWindowVisible() && reCreateWindow) {
- deleteWindowMagnification();
+ deleteWindowMagnification(/* closeSettingPanel= */ false);
enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 9ad64e29..d9f5544 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -230,14 +230,6 @@
}
};
- private void applyResourcesValuesWithDensityChanged() {
- if (mIsVisible) {
- // Reset button to make its window layer always above the mirror window.
- hideSettingPanel();
- showSettingPanel(false);
- }
- }
-
private boolean onTouch(View v, MotionEvent event) {
if (!mIsVisible) {
return false;
@@ -354,10 +346,6 @@
}
mWindowManager.addView(mSettingView, mParams);
- if (resetPosition) {
- // Request focus on the settings panel when position of the panel is reset.
- mSettingView.requestFocus();
- }
// Exclude magnification switch button from system gesture area.
setSystemGestureExclusion();
@@ -445,7 +433,15 @@
void onConfigurationChanged(int configDiff) {
if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0
- || (configDiff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
+ || (configDiff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0
+ || (configDiff & ActivityInfo.CONFIG_FONT_SCALE) != 0
+ || (configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
+ // We listen to following config changes to trigger layout inflation:
+ // CONFIG_UI_MODE: theme change
+ // CONFIG_ASSETS_PATHS: wallpaper change
+ // CONFIG_FONT_SCALE: font size change
+ // CONFIG_DENSITY: display size change
+
boolean showSettingPanelAfterThemeChange = mIsVisible;
hideSettingPanel(/* resetPosition= */ false);
inflateView();
@@ -454,6 +450,7 @@
}
return;
}
+
if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
final Rect previousDraggableBounds = new Rect(mDraggableWindowBounds);
mDraggableWindowBounds.set(getDraggableWindowBounds());
@@ -465,10 +462,7 @@
+ mDraggableWindowBounds.top;
return;
}
- if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
- applyResourcesValuesWithDensityChanged();
- return;
- }
+
if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
updateAccessibilityWindowTitle();
return;
@@ -536,7 +530,7 @@
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
- /* _flags= */ 0,
+ LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT);
params.gravity = Gravity.TOP | Gravity.START;
params.accessibilityTitle = getAccessibilityWindowTitle(context);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 08efd89..c31d45f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -148,6 +148,7 @@
@NonNull private final WindowManager mWindowManager;
@NonNull private final DisplayManager mDisplayManager;
@Nullable private UdfpsController mUdfpsController;
+ @Nullable private UdfpsOverlayParams mUdfpsOverlayParams;
@Nullable private IUdfpsRefreshRateRequestCallback mUdfpsRefreshRateRequestCallback;
@Nullable private SideFpsController mSideFpsController;
@Nullable private UdfpsLogger mUdfpsLogger;
@@ -806,6 +807,8 @@
final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0);
final Rect previousUdfpsBounds = mUdfpsBounds;
+ final UdfpsOverlayParams previousUdfpsOverlayParams = mUdfpsOverlayParams;
+
mUdfpsBounds = udfpsProp.getLocation().getRect();
mUdfpsBounds.scale(mScaleFactor);
@@ -815,7 +818,7 @@
mCachedDisplayInfo.getNaturalWidth(), /* right */
mCachedDisplayInfo.getNaturalHeight() /* botom */);
- final UdfpsOverlayParams overlayParams = new UdfpsOverlayParams(
+ mUdfpsOverlayParams = new UdfpsOverlayParams(
mUdfpsBounds,
overlayBounds,
mCachedDisplayInfo.getNaturalWidth(),
@@ -823,10 +826,11 @@
mScaleFactor,
mCachedDisplayInfo.rotation);
- mUdfpsController.updateOverlayParams(udfpsProp, overlayParams);
- if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds)) {
+ mUdfpsController.updateOverlayParams(udfpsProp, mUdfpsOverlayParams);
+ if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds) || !Objects.equals(
+ previousUdfpsOverlayParams, mUdfpsOverlayParams)) {
for (Callback cb : mCallbacks) {
- cb.onUdfpsLocationChanged();
+ cb.onUdfpsLocationChanged(mUdfpsOverlayParams);
}
}
}
@@ -1336,7 +1340,7 @@
* On devices with UDFPS, this is always called alongside
* {@link #onFingerprintLocationChanged}.
*/
- default void onUdfpsLocationChanged() {}
+ default void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {}
/**
* Called when the location of the face unlock sensor (typically the front facing camera)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 93b57dc..1719ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -28,6 +28,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.logging.KeyguardLogger
import com.android.settingslib.Utils
+import com.android.settingslib.udfps.UdfpsOverlayParams
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.flags.FeatureFlags
@@ -326,7 +327,7 @@
updateUdfpsDependentParams()
}
- override fun onUdfpsLocationChanged() {
+ override fun onUdfpsLocationChanged(udfpsOverlayParams: UdfpsOverlayParams) {
updateUdfpsDependentParams()
}
}
@@ -397,7 +398,7 @@
}
companion object {
- const val RIPPLE_ANIMATION_DURATION: Long = 1533
+ const val RIPPLE_ANIMATION_DURATION: Long = 800
const val TAG = "AuthRippleController"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 4b32759..8409462 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -31,7 +31,7 @@
import com.android.systemui.animation.Interpolators
import com.android.systemui.surfaceeffects.ripple.RippleShader
-private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
+private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
/**
* Handles two ripple effects: dwell ripple and unlocked ripple
@@ -75,8 +75,8 @@
}
private var radius: Float = 0f
set(value) {
- rippleShader.rippleSize.setMaxSize(value * 2f, value * 2f)
- field = value
+ field = value * .9f
+ rippleShader.rippleSize.setMaxSize(field * 2f, field * 2f)
}
private var origin: Point = Point()
set(value) {
@@ -87,8 +87,9 @@
init {
rippleShader.color = 0xffffffff.toInt() // default color
rippleShader.rawProgress = 0f
+ rippleShader.pixelDensity = resources.displayMetrics.density
rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
- setupRippleFadeParams()
+ updateRippleFadeParams()
ripplePaint.shader = rippleShader
dwellShader.color = 0xffffffff.toInt() // default color
@@ -266,7 +267,6 @@
unlockedRippleAnimator?.cancel()
val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
- interpolator = Interpolators.LINEAR_OUT_SLOW_IN
duration = AuthRippleController.RIPPLE_ANIMATION_DURATION
addUpdateListener { animator ->
val now = animator.currentPlayTime
@@ -277,7 +277,7 @@
}
}
- val alphaInAnimator = ValueAnimator.ofInt(0, 255).apply {
+ val alphaInAnimator = ValueAnimator.ofInt(0, 62).apply {
duration = alphaInDuration
addUpdateListener { animator ->
rippleShader.color = ColorUtils.setAlphaComponent(
@@ -339,15 +339,17 @@
)
}
- private fun setupRippleFadeParams() {
+ private fun updateRippleFadeParams() {
with(rippleShader) {
- baseRingFadeParams.fadeOutStart = RippleShader.DEFAULT_BASE_RING_FADE_OUT_START
- baseRingFadeParams.fadeOutEnd = RippleShader.DEFAULT_FADE_OUT_END
+ baseRingFadeParams.fadeInStart = 0f
+ baseRingFadeParams.fadeInEnd = .2f
+ baseRingFadeParams.fadeOutStart = .2f
+ baseRingFadeParams.fadeOutEnd = 1f
- 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
+ centerFillFadeParams.fadeInStart = 0f
+ centerFillFadeParams.fadeInEnd = .15f
+ centerFillFadeParams.fadeOutStart = .15f
+ centerFillFadeParams.fadeOutEnd = .56f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index f7d87fc..b62c729 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -64,6 +64,7 @@
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.traceSection
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -215,7 +216,9 @@
requests.add(request)
mainExecutor.execute {
if (overlayView == null) {
- createOverlayForDisplay(reason)
+ traceSection("SideFpsController#show(request=${request.name}, reason=$reason") {
+ createOverlayForDisplay(reason)
+ }
} else {
Log.v(TAG, "overlay already shown")
}
@@ -227,7 +230,7 @@
requests.remove(request)
mainExecutor.execute {
if (requests.isEmpty()) {
- overlayView = null
+ traceSection("SideFpsController#hide(${request.name}") { overlayView = null }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9e83264..bb35355 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -100,6 +100,8 @@
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.SystemClock;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -110,8 +112,6 @@
import javax.inject.Inject;
import javax.inject.Provider;
-import kotlin.Unit;
-
/**
* Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
* and toggles the UDFPS display mode.
@@ -598,14 +598,20 @@
mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
break;
+ case UNCHANGED:
+ if (!isWithinSensorArea(mOverlay.getOverlayView(), event.getX(), event.getY(),
+ true) && mActivePointerId == MotionEvent.INVALID_POINTER_ID
+ && event.getActionMasked() == MotionEvent.ACTION_DOWN
+ && mAlternateBouncerInteractor.isVisibleState()) {
+ // No pointer on sensor, forward to keyguard if alternateBouncer is visible
+ mKeyguardViewManager.onTouch(event);
+ }
+
default:
break;
}
logBiometricTouch(processedTouch.getEvent(), data);
- // We should only consume touches that are within the sensor. By returning "false" for
- // touches outside of the sensor, we let other UI components consume these events and act on
- // them appropriately.
return processedTouch.getTouchData().isWithinSensor(mOverlayParams.getNativeSensorBounds());
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
new file mode 100644
index 0000000..92a7094
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.biometrics.domain.interactor
+
+import android.view.MotionEvent
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.settingslib.udfps.UdfpsOverlayParams
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Encapsulates business logic for interacting with the UDFPS overlay. */
+@SysUISingleton
+class UdfpsOverlayInteractor
+@Inject
+constructor(private val authController: AuthController, @Application scope: CoroutineScope) {
+
+ /** Whether a touch should be intercepted or allowed to pass to the UdfpsOverlay */
+ fun canInterceptTouchInUdfpsBounds(ev: MotionEvent): Boolean {
+ val isUdfpsEnrolled = authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())
+ val isWithinUdfpsOverlayBounds =
+ udfpsOverlayParams.value.overlayBounds.contains(ev.rawX.toInt(), ev.rawY.toInt())
+ return !isUdfpsEnrolled || !isWithinUdfpsOverlayBounds
+ }
+
+ /** Returns the current udfpsOverlayParams */
+ val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback =
+ object : AuthController.Callback {
+ override fun onUdfpsLocationChanged(
+ udfpsOverlayParams: UdfpsOverlayParams
+ ) {
+ trySendWithFailureLogging(
+ udfpsOverlayParams,
+ TAG,
+ "update udfpsOverlayParams"
+ )
+ }
+ }
+ authController.addCallback(callback)
+ awaitClose { authController.removeCallback(callback) }
+ }
+ .stateIn(scope, started = SharingStarted.Eagerly, initialValue = UdfpsOverlayParams())
+
+ companion object {
+ private const val TAG = "UdfpsOverlayInteractor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
index b9f1666..cf5ccc5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.management
+import android.annotation.WorkerThread
import android.content.ComponentName
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.util.UserAwareController
@@ -33,6 +34,9 @@
*/
fun getCurrentServices(): List<ControlsServiceInfo>
+ @WorkerThread
+ fun forceReload()
+
/**
* Get the app label for a given component.
*
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index c81a2c7..8ba060e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -16,8 +16,11 @@
package com.android.systemui.controls.management
+import android.annotation.WorkerThread
import android.content.ComponentName
import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
import android.os.UserHandle
import android.service.controls.ControlsProviderService
import android.util.Log
@@ -65,7 +68,7 @@
private val serviceListingBuilder: (Context) -> ServiceListing,
private val userTracker: UserTracker,
dumpManager: DumpManager,
- featureFlags: FeatureFlags
+ private val featureFlags: FeatureFlags
) : ControlsListingController, Dumpable {
@Inject
@@ -97,18 +100,7 @@
// After here, `list` is not captured, so we don't risk modifying it outside of the callback
backgroundExecutor.execute {
if (userChangeInProgress.get() > 0) return@execute
- if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
- val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
- newServices.forEach {
- it.resolvePanelActivity(allowAllApps) }
- }
-
- if (newServices != availableServices) {
- availableServices = newServices
- callbacks.forEach {
- it.onServicesUpdated(getCurrentServices())
- }
- }
+ updateServices(newServices)
}
}
@@ -120,6 +112,21 @@
serviceListing.reload()
}
+ private fun updateServices(newServices: List<ControlsServiceInfo>) {
+ if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
+ val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
+ newServices.forEach {
+ it.resolvePanelActivity(allowAllApps) }
+ }
+
+ if (newServices != availableServices) {
+ availableServices = newServices
+ callbacks.forEach {
+ it.onServicesUpdated(getCurrentServices())
+ }
+ }
+ }
+
override fun changeUser(newUser: UserHandle) {
userChangeInProgress.incrementAndGet()
serviceListing.setListening(false)
@@ -178,6 +185,23 @@
override fun getCurrentServices(): List<ControlsServiceInfo> =
availableServices.map(ControlsServiceInfo::copy)
+ @WorkerThread
+ override fun forceReload() {
+ val packageManager = context.packageManager
+ val intent = Intent(ControlsProviderService.SERVICE_CONTROLS)
+ val user = userTracker.userHandle
+ val flags = PackageManager.GET_SERVICES or
+ PackageManager.GET_META_DATA or
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE or
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ val services = packageManager.queryIntentServicesAsUser(
+ intent,
+ PackageManager.ResolveInfoFlags.of(flags.toLong()),
+ user
+ ).map { ControlsServiceInfo(userTracker.userContext, it.serviceInfo) }
+ updateServices(services)
+ }
+
/**
* Get the localized label for the component.
*
diff --git a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
index 3a4a00c..461cacc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.os.UserHandle
+import androidx.annotation.WorkerThread
import com.android.systemui.CoreStartable
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.dagger.ControlsComponent
@@ -75,11 +76,13 @@
// Controls is disabled, we don't need this anymore
return
}
- startForUser()
+ executor.execute(this::startForUser)
userTracker.addCallback(userTrackerCallback, executor)
}
+ @WorkerThread
private fun startForUser() {
+ controlsListingController.forceReload()
selectDefaultPanelIfNecessary()
bindToPanel()
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
index 24e90f0..aad2090 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
@@ -61,9 +61,6 @@
@VisibleForTesting
boolean mIsAnimationEnabled;
- // Whether dream entry animations are finished.
- private boolean mEntryAnimationsFinished = false;
-
@Inject
protected ComplicationHostViewController(
@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout view,
@@ -78,14 +75,6 @@
mComplicationCollectionViewModel = viewModel;
mDreamOverlayStateController = dreamOverlayStateController;
- mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
- @Override
- public void onStateChanged() {
- mEntryAnimationsFinished =
- mDreamOverlayStateController.areEntryAnimationsFinished();
- }
- });
-
// Whether animations are enabled.
mIsAnimationEnabled = secureSettings.getFloatForUser(
Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f, UserHandle.USER_CURRENT) != 0.0f;
@@ -159,7 +148,8 @@
// Complications to be added before dream entry animations are finished are set
// to invisible and are animated in.
- if (!mEntryAnimationsFinished && mIsAnimationEnabled) {
+ if (!mDreamOverlayStateController.areEntryAnimationsFinished()
+ && mIsAnimationEnabled) {
view.setVisibility(View.INVISIBLE);
}
mComplications.put(id, viewHolder);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b6edcf5..265a9903 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -137,7 +137,7 @@
* the digits when the clock moves.
*/
@JvmField
- val STEP_CLOCK_ANIMATION = unreleasedFlag(212, "step_clock_animation", teamfood = true)
+ val STEP_CLOCK_ANIMATION = releasedFlag(212, "step_clock_animation")
/**
* Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
@@ -220,6 +220,11 @@
"lock_screen_long_press_enabled"
)
+ /** Whether to inflate the bouncer view on a background thread. */
+ // TODO(b/272091103): Tracking Bug
+ @JvmField
+ val ASYNC_INFLATE_BOUNCER = unreleasedFlag(229, "async_inflate_bouncer", teamfood = true)
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -485,6 +490,13 @@
val ENABLE_PIP_APP_ICON_OVERLAY =
sysPropBooleanFlag(1115, "persist.wm.debug.enable_pip_app_icon_overlay", default = true)
+ // TODO(b/272110828): Tracking bug
+ @Keep
+ @JvmField
+ val ENABLE_MOVE_FLOATING_WINDOW_IN_TABLETOP =
+ sysPropBooleanFlag(
+ 1116, "persist.wm.debug.enable_move_floating_window_in_tabletop", default = false)
+
// 1200 - predictive back
@Keep
@JvmField
@@ -616,14 +628,12 @@
@JvmField val UDFPS_ELLIPSE_DETECTION = releasedFlag(2201, "udfps_ellipse_detection")
// 2300 - stylus
+ @JvmField val TRACK_STYLUS_EVER_USED = releasedFlag(2300, "track_stylus_ever_used")
@JvmField
- val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used", teamfood = true)
- @JvmField
- val ENABLE_STYLUS_CHARGING_UI =
- unreleasedFlag(2301, "enable_stylus_charging_ui", teamfood = true)
+ val ENABLE_STYLUS_CHARGING_UI = releasedFlag(2301, "enable_stylus_charging_ui")
@JvmField
val ENABLE_USI_BATTERY_NOTIFICATIONS =
- unreleasedFlag(2302, "enable_usi_battery_notifications", teamfood = true)
+ releasedFlag(2302, "enable_usi_battery_notifications")
@JvmField val ENABLE_STYLUS_EDUCATION = unreleasedFlag(2303, "enable_stylus_education")
// 2400 - performance tools and debugging info
@@ -663,5 +673,5 @@
// TODO(b/272036292): Tracking Bug
@JvmField
val LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION =
- unreleasedFlag(2602, "large_shade_granular_alpha_interpolation")
+ unreleasedFlag(2602, "large_shade_granular_alpha_interpolation", teamfood = true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 57c4b36..3e52ff2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -380,10 +380,7 @@
// If the launcher is underneath, but we're about to launch an activity, don't do
// the animations since they won't be visible.
!notificationShadeWindowController.isLaunchingActivity &&
- launcherUnlockController != null &&
- // Temporarily disable for foldables since foldable launcher has two first pages,
- // which breaks the in-window animation.
- !isFoldable(context)
+ launcherUnlockController != null
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 911861d..28cc697 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -64,7 +64,11 @@
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (isAbleToDream, lastStartedTransition) = pair
- if (isAbleToDream && lastStartedTransition.to == KeyguardState.LOCKSCREEN) {
+ if (
+ isAbleToDream &&
+ lastStartedTransition.to == KeyguardState.LOCKSCREEN &&
+ lastStartedTransition.from != KeyguardState.AOD
+ ) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
index 4880f80..b73ddc5 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
@@ -59,6 +59,17 @@
int = value
}
+ /** Updates this to store the same value as [change]. */
+ fun updateTo(change: TableChange) {
+ reset(change.timestamp, change.columnPrefix, change.columnName)
+ when (change.type) {
+ DataType.STRING -> set(change.str)
+ DataType.INT -> set(change.int)
+ DataType.BOOLEAN -> set(change.bool)
+ DataType.EMPTY -> {}
+ }
+ }
+
/** Returns true if this object has a change. */
fun hasData(): Boolean {
return columnName.isNotBlank() && type != DataType.EMPTY
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 29f273a..a0f1c95 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -16,13 +16,13 @@
package com.android.systemui.log.table
+import android.os.Trace
import com.android.systemui.Dumpable
import com.android.systemui.plugins.util.RingBuffer
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import java.text.SimpleDateFormat
import java.util.Locale
-import kotlinx.coroutines.flow.Flow
/**
* A logger that logs changes in table format.
@@ -82,6 +82,19 @@
private val buffer = RingBuffer(maxSize) { TableChange() }
+ // Stores the most recently evicted value for each column name (indexed on column name).
+ //
+ // Why it's necessary: Because we use a RingBuffer of a fixed size, it's possible that a column
+ // that's logged infrequently will eventually get pushed out by a different column that's
+ // logged more frequently. Then, that infrequently-logged column isn't present in the RingBuffer
+ // at all and we have no logs that the column ever existed. This is a problem because the
+ // column's information is still relevant, valid, and may be critical to debugging issues.
+ //
+ // Fix: When a change is being evicted from the RingBuffer, we store it in this map (based on
+ // its [TableChange.getName()]. This ensures that we always have at least one value for every
+ // column ever logged. See b/272016422 for more details.
+ private val lastEvictedValues = mutableMapOf<String, TableChange>()
+
// A [TableRowLogger] object, re-used each time [logDiffs] is called.
// (Re-used to avoid object allocation.)
private val tempRow = TableRowLoggerImpl(0, columnPrefix = "", this)
@@ -138,18 +151,24 @@
// timestamps.)
private fun logChange(timestamp: Long, prefix: String, columnName: String, value: String?) {
+ Trace.beginSection("TableLogBuffer#logChange(string)")
val change = obtain(timestamp, prefix, columnName)
change.set(value)
+ Trace.endSection()
}
private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Boolean) {
+ Trace.beginSection("TableLogBuffer#logChange(boolean)")
val change = obtain(timestamp, prefix, columnName)
change.set(value)
+ Trace.endSection()
}
private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Int?) {
+ Trace.beginSection("TableLogBuffer#logChange(int)")
val change = obtain(timestamp, prefix, columnName)
change.set(value)
+ Trace.endSection()
}
// TODO(b/259454430): Add additional change types here.
@@ -158,6 +177,9 @@
private fun obtain(timestamp: Long, prefix: String, columnName: String): TableChange {
verifyValidName(prefix, columnName)
val tableChange = buffer.advance()
+ if (tableChange.hasData()) {
+ saveEvictedValue(tableChange)
+ }
tableChange.reset(timestamp, prefix, columnName)
return tableChange
}
@@ -173,10 +195,23 @@
}
}
+ private fun saveEvictedValue(change: TableChange) {
+ Trace.beginSection("TableLogBuffer#saveEvictedValue")
+ val name = change.getName()
+ val previouslyEvicted =
+ lastEvictedValues[name] ?: TableChange().also { lastEvictedValues[name] = it }
+ // For recycling purposes, update the existing object in the map with the new information
+ // instead of creating a new object.
+ previouslyEvicted.updateTo(change)
+ Trace.endSection()
+ }
+
@Synchronized
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println(HEADER_PREFIX + name)
pw.println("version $VERSION")
+
+ lastEvictedValues.values.sortedBy { it.timestamp }.forEach { it.dump(pw) }
for (i in 0 until buffer.size) {
buffer[i].dump(pw)
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index 1c90154..9bccb7d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -24,6 +24,7 @@
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
+import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -61,8 +62,15 @@
val width = windowMetrics.bounds.width()
var height = maximumWindowHeight
+ // TODO(b/271410803): Read isTransientTaskbar from Launcher
val isLargeScreen = isLargeScreen(context)
- if (isLargeScreen) {
+ val isTransientTaskbar =
+ QuickStepContract.isGesturalMode(
+ context.resources.getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode
+ )
+ )
+ if (isLargeScreen && !isTransientTaskbar) {
val taskbarSize =
context.resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
height -= taskbarSize
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index e4351d2..df70f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -839,6 +839,7 @@
}
mFrame = null;
mOrientationHandle = null;
+ notifyNavigationBarSurface();
}
// TODO: Remove this when we update nav bar recreation
@@ -1003,7 +1004,8 @@
private void notifyNavigationBarSurface() {
ViewRootImpl viewRoot = mView.getViewRootImpl();
- SurfaceControl surface = viewRoot != null
+ SurfaceControl surface = mView.getParent() != null
+ && viewRoot != null
&& viewRoot.getSurfaceControl() != null
&& viewRoot.getSurfaceControl().isValid()
? viewRoot.getSurfaceControl()
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 6cd04c8..ac22b7c 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -89,22 +89,16 @@
/**
* Shows a note task. How the task is shown will depend on when the method is invoked.
*
- * If in multi-window mode, notes will open as a full screen experience. That is particularly
- * important for Large screen devices. These devices may support a taskbar that let users to
- * drag and drop a shortcut into multi-window mode, and notes should comply with this behaviour.
- *
* If the keyguard is locked, notes will open as a full screen experience. A locked device has
* no contextual information which let us use the whole screen space available.
*
- * If not in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
- * collapsed if the notes bubble is already opened.
+ * If the keyguard is unlocked, notes will open as a bubble OR it will be collapsed if the notes
+ * bubble is already opened.
*
* That will let users open other apps in full screen, and take contextual notes.
*/
- @JvmOverloads
fun showNoteTask(
entryPoint: NoteTaskEntryPoint,
- isInMultiWindowMode: Boolean = false,
) {
if (!isEnabled) return
@@ -125,13 +119,7 @@
return
}
- val info =
- resolver.resolveInfo(
- entryPoint = entryPoint,
- isInMultiWindowMode = isInMultiWindowMode,
- isKeyguardLocked = isKeyguardLocked,
- )
- ?: return
+ val info = resolver.resolveInfo(entryPoint, isKeyguardLocked) ?: return
infoReference.set(info)
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
index 28d7647..2b9f0af 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
@@ -20,12 +20,11 @@
val packageName: String,
val uid: Int,
val entryPoint: NoteTaskEntryPoint? = null,
- val isInMultiWindowMode: Boolean = false,
val isKeyguardLocked: Boolean = false,
) {
val launchMode: NoteTaskLaunchMode =
- if (isInMultiWindowMode || isKeyguardLocked) {
+ if (isKeyguardLocked) {
NoteTaskLaunchMode.Activity
} else {
NoteTaskLaunchMode.AppBubble
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
index b98a0fd..8ecf081 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
@@ -34,7 +34,6 @@
fun resolveInfo(
entryPoint: NoteTaskEntryPoint? = null,
- isInMultiWindowMode: Boolean = false,
isKeyguardLocked: Boolean = false,
): NoteTaskInfo? {
// TODO(b/267634412): Select UserHandle depending on where the user initiated note-taking.
@@ -48,7 +47,6 @@
packageName = packageName,
uid = packageManager.getUidOf(packageName, user),
entryPoint = entryPoint,
- isInMultiWindowMode = isInMultiWindowMode,
isKeyguardLocked = isKeyguardLocked,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 30660c4..8aed995 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -17,6 +17,7 @@
package com.android.systemui.notetask.quickaffordance
import android.content.Context
+import android.hardware.input.InputSettings
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
@@ -26,36 +27,52 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
+import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEnabledKey
import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.stylus.StylusManager
+import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.trySendBlocking
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
class NoteTaskQuickAffordanceConfig
@Inject
constructor(
context: Context,
- private val noteTaskController: NoteTaskController,
+ private val controller: NoteTaskController,
+ private val stylusManager: StylusManager,
+ private val lazyRepository: Lazy<KeyguardQuickAffordanceRepository>,
@NoteTaskEnabledKey private val isEnabled: Boolean,
) : KeyguardQuickAffordanceConfig {
override val key = BuiltInKeyguardQuickAffordanceKeys.CREATE_NOTE
- override val pickerName: String = context.getString(R.string.note_task_button_label)
+ private val pickerNameResourceId = R.string.note_task_button_label
+
+ override val pickerName: String = context.getString(pickerNameResourceId)
override val pickerIconResourceId = R.drawable.ic_note_task_shortcut_keyguard
- override val lockScreenState = flowOf(getLockScreenState())
-
- // TODO(b/265949213)
- private fun getLockScreenState() =
- if (isEnabled) {
- val icon = Icon.Resource(pickerIconResourceId, ContentDescription.Loaded(pickerName))
- LockScreenState.Visible(icon)
- } else {
- LockScreenState.Hidden
+ // Due to a dependency cycle with KeyguardQuickAffordanceRepository, we need to lazily access
+ // the repository when lockScreenState is accessed for the first time.
+ override val lockScreenState by lazy {
+ val stylusEverUsedFlow = createStylusEverUsedFlow(context, stylusManager)
+ val configSelectedFlow = createConfigSelectedFlow(lazyRepository.get(), key)
+ combine(configSelectedFlow, stylusEverUsedFlow) { isSelected, isStylusEverUsed ->
+ if (isEnabled && (isSelected || isStylusEverUsed)) {
+ val contentDescription = ContentDescription.Resource(pickerNameResourceId)
+ val icon = Icon.Resource(pickerIconResourceId, contentDescription)
+ LockScreenState.Visible(icon)
+ } else {
+ LockScreenState.Hidden
+ }
}
+ }
override suspend fun getPickerScreenState() =
if (isEnabled) {
@@ -65,9 +82,27 @@
}
override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
- noteTaskController.showNoteTask(
+ controller.showNoteTask(
entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE,
)
return OnTriggeredResult.Handled
}
}
+
+private fun createStylusEverUsedFlow(context: Context, stylusManager: StylusManager) =
+ callbackFlow {
+ trySendBlocking(InputSettings.isStylusEverUsed(context))
+ val callback =
+ object : StylusManager.StylusCallback {
+ override fun onStylusFirstUsed() {
+ trySendBlocking(InputSettings.isStylusEverUsed(context))
+ }
+ }
+ stylusManager.registerCallback(callback)
+ awaitClose { stylusManager.unregisterCallback(callback) }
+ }
+
+private fun createConfigSelectedFlow(repository: KeyguardQuickAffordanceRepository, key: String) =
+ repository.selections.map { selected ->
+ selected.values.flatten().any { selectedConfig -> selectedConfig.key == key }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
index 80fce6a..14b0779 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
@@ -28,17 +28,12 @@
class LaunchNoteTaskActivity
@Inject
constructor(
- private val noteTaskController: NoteTaskController,
+ private val controller: NoteTaskController,
) : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
-
- noteTaskController.showNoteTask(
- entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT,
- isInMultiWindowMode = isInMultiWindowMode,
- )
-
+ controller.showNoteTask(entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT)
finish()
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 019ca52..8a3ecc6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -31,6 +31,8 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_ON;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_TRANSITION;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -121,7 +123,8 @@
public class OverviewProxyService implements CallbackController<OverviewProxyListener>,
NavigationModeController.ModeChangedListener, Dumpable {
- private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
+ @VisibleForTesting
+ static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
public static final String TAG_OPS = "OverviewProxyService";
private static final long BACKOFF_MILLIS = 1000;
@@ -548,6 +551,7 @@
mUiEventLogger = uiEventLogger;
mDisplayTracker = displayTracker;
mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
+ mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
dumpManager.registerDumpable(getClass().getSimpleName(), this);
@@ -596,7 +600,6 @@
// Connect to the service
updateEnabledState();
startConnectionToCurrentUser();
- mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
// Listen for assistant changes
assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener);
@@ -726,10 +729,8 @@
return;
}
mHandler.removeCallbacks(mConnectionRunnable);
- Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
- .setPackage(mRecentsComponentName.getPackageName());
try {
- mBound = mContext.bindServiceAsUser(launcherServiceIntent,
+ mBound = mContext.bindServiceAsUser(mQuickStepIntent,
mOverviewServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
UserHandle.of(mUserTracker.getUserId()));
@@ -862,6 +863,11 @@
*/
@Override
public void onScreenTurnedOn() {
+ mSysUiState
+ .setFlag(SYSUI_STATE_SCREEN_ON, true)
+ .setFlag(SYSUI_STATE_SCREEN_TRANSITION, false)
+ .commitUpdate(mContext.getDisplayId());
+
try {
if (mOverviewProxy != null) {
mOverviewProxy.onScreenTurnedOn();
@@ -874,10 +880,26 @@
}
/**
+ * Notifies the Launcher that screen turned off.
+ */
+ @Override
+ public void onScreenTurnedOff() {
+ mSysUiState
+ .setFlag(SYSUI_STATE_SCREEN_ON, false)
+ .setFlag(SYSUI_STATE_SCREEN_TRANSITION, false)
+ .commitUpdate(mContext.getDisplayId());
+ }
+
+ /**
* Notifies the Launcher that screen is starting to turn on.
*/
@Override
public void onScreenTurningOff() {
+ mSysUiState
+ .setFlag(SYSUI_STATE_SCREEN_ON, false)
+ .setFlag(SYSUI_STATE_SCREEN_TRANSITION, true)
+ .commitUpdate(mContext.getDisplayId());
+
try {
if (mOverviewProxy != null) {
mOverviewProxy.onScreenTurningOff();
@@ -894,6 +916,11 @@
*/
@Override
public void onScreenTurningOn() {
+ mSysUiState
+ .setFlag(SYSUI_STATE_SCREEN_ON, true)
+ .setFlag(SYSUI_STATE_SCREEN_TRANSITION, true)
+ .commitUpdate(mContext.getDisplayId());
+
try {
if (mOverviewProxy != null) {
mOverviewProxy.onScreenTurningOn();
@@ -1005,4 +1032,21 @@
default void onAssistantGestureCompletion(float velocity) {}
default void startAssistant(Bundle bundle) {}
}
+
+ /**
+ * Shuts down this service at the end of a testcase.
+ * <p>
+ * The in-production service is never shuts down, and it was not designed with testing in mind.
+ * This unregisters the mechanisms by which the service will be revived after a testcase.
+ * <p>
+ * NOTE: This is a stop-gap introduced when first added some tests to this class. It should
+ * probably be replaced by proper lifecycle management on this class.
+ */
+ @VisibleForTesting()
+ void shutdownForTest() {
+ mContext.unregisterReceiver(mLauncherStateChangedReceiver);
+ mIsEnabled = false;
+ mHandler.removeCallbacks(mConnectionRunnable);
+ disconnectFromLauncherService();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index c130b39..c09524b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -36,6 +36,7 @@
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -86,7 +87,7 @@
private final PulsingGestureListener mPulsingGestureListener;
private final NotificationInsetsController mNotificationInsetsController;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
-
+ private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
private GestureDetector mPulsingWakeupGestureHandler;
private View mBrightnessMirror;
private boolean mTouchActive;
@@ -134,6 +135,7 @@
KeyguardBouncerViewModel keyguardBouncerViewModel,
KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
AlternateBouncerInteractor alternateBouncerInteractor,
+ UdfpsOverlayInteractor udfpsOverlayInteractor,
KeyguardTransitionInteractor keyguardTransitionInteractor,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel
) {
@@ -156,6 +158,7 @@
mPulsingGestureListener = pulsingGestureListener;
mNotificationInsetsController = notificationInsetsController;
mAlternateBouncerInteractor = alternateBouncerInteractor;
+ mUdfpsOverlayInteractor = udfpsOverlayInteractor;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -240,7 +243,6 @@
mFalsingCollector.onTouchEvent(ev);
mPulsingWakeupGestureHandler.onTouchEvent(ev);
- mStatusBarKeyguardViewManager.onTouch(ev);
if (mBrightnessMirror != null
&& mBrightnessMirror.getVisibility() == View.VISIBLE) {
// Disallow new pointers while the brightness mirror is visible. This is so that
@@ -316,8 +318,8 @@
}
if (mAlternateBouncerInteractor.isVisibleState()) {
- // capture all touches if the alt auth bouncer is showing
- return true;
+ // If using UDFPS, don't intercept touches that are within its overlay bounds
+ return mUdfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(ev);
}
if (mLockIconViewController.onInterceptTouchEvent(ev)) {
@@ -355,6 +357,7 @@
if (mAlternateBouncerInteractor.isVisibleState()) {
// eat the touch
+ mStatusBarKeyguardViewManager.onTouch(ev);
handled = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 6857f4c..df8ae50 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -1072,7 +1072,11 @@
private void setClippingBounds() {
float qsExpansionFraction = computeExpansionFraction();
final int qsPanelBottomY = calculateBottomPosition(qsExpansionFraction);
- final boolean qsVisible = (qsExpansionFraction > 0 || qsPanelBottomY > 0);
+ // Split shade has no QQS
+ final boolean qqsVisible =
+ !mSplitShadeEnabled && qsExpansionFraction == 0 && qsPanelBottomY > 0;
+ final boolean qsVisible = qsExpansionFraction > 0;
+ final boolean qsOrQqsVisible = qqsVisible || qsVisible;
checkCorrectScrimVisibility(qsExpansionFraction);
int top = calculateTopClippingBound(qsPanelBottomY);
@@ -1081,7 +1085,7 @@
int right = calculateRightClippingBound();
// top should never be lower than bottom, otherwise it will be invisible.
top = Math.min(top, bottom);
- applyClippingBounds(left, top, right, bottom, qsVisible);
+ applyClippingBounds(left, top, right, bottom, qsOrQqsVisible);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 664d61a..00519b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3623,7 +3623,8 @@
boolean goingToSleepWithoutAnimation = isGoingToSleep()
&& !mDozeParameters.shouldControlScreenOff();
boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
- || goingToSleepWithoutAnimation;
+ || goingToSleepWithoutAnimation
+ || mDeviceProvisionedController.isFrpActive();
mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
mNotificationIconAreaController.setAnimationsEnabled(!disabled);
}
@@ -3797,10 +3798,10 @@
boolean launchingAffordanceWithPreview = mLaunchingAffordance;
mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
-
if (mAlternateBouncerInteractor.isVisibleState()) {
- if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
- || mTransitionToFullShadeProgress > 0f) {
+ if ((!isOccluded() || isPanelExpanded())
+ && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+ || mTransitionToFullShadeProgress > 0f)) {
mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
} else {
mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 66f5b65..aa71b51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -733,7 +733,9 @@
} else {
showBouncerOrKeyguard(hideBouncerWhenShowing);
}
- hideAlternateBouncer(false);
+ if (hideBouncerWhenShowing) {
+ hideAlternateBouncer(false);
+ }
mKeyguardUpdateManager.sendKeyguardReset();
updateStates();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
index 3944c8c..0d09fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
@@ -21,7 +21,8 @@
/**
* Controller to cache in process the state of the device provisioning.
* <p>
- * This controller keeps track of the values of device provisioning and user setup complete
+ * This controller keeps track of the values of device provisioning, user setup complete, and
+ * whether Factory Reset Protection is active.
*/
public interface DeviceProvisionedController extends CallbackController<DeviceProvisionedListener> {
@@ -49,6 +50,9 @@
*/
boolean isCurrentUserSetup();
+ /** Returns true when Factory Reset Protection is locking the device. */
+ boolean isFrpActive();
+
/**
* Interface to provide calls when the values tracked change
*/
@@ -69,5 +73,10 @@
* Call when some user changes from not provisioned to provisioned
*/
default void onUserSetupChanged() { }
+
+ /**
+ * Called when the state of FRP changes.
+ */
+ default void onFrpActiveChanged() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
index a6b7d9c5..32c64f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -60,9 +60,11 @@
}
private val deviceProvisionedUri = globalSettings.getUriFor(Settings.Global.DEVICE_PROVISIONED)
+ private val frpActiveUri = secureSettings.getUriFor(Settings.Secure.SECURE_FRP_MODE)
private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
private val deviceProvisioned = AtomicBoolean(false)
+ private val frpActive = AtomicBoolean(false)
@GuardedBy("lock")
private val userSetupComplete = SparseBooleanArray()
@GuardedBy("lock")
@@ -89,11 +91,15 @@
userId: Int
) {
val updateDeviceProvisioned = deviceProvisionedUri in uris
+ val updateFrp = frpActiveUri in uris
val updateUser = if (userSetupUri in uris) userId else NO_USERS
- updateValues(updateDeviceProvisioned, updateUser)
+ updateValues(updateDeviceProvisioned, updateFrp, updateUser)
if (updateDeviceProvisioned) {
onDeviceProvisionedChanged()
}
+ if (updateFrp) {
+ onFrpActiveChanged()
+ }
if (updateUser != NO_USERS) {
onUserSetupChanged()
}
@@ -103,7 +109,7 @@
private val userChangedCallback = object : UserTracker.Callback {
@WorkerThread
override fun onUserChanged(newUser: Int, userContext: Context) {
- updateValues(updateDeviceProvisioned = false, updateUser = newUser)
+ updateValues(updateDeviceProvisioned = false, updateFrp = false, updateUser = newUser)
onUserSwitched()
}
@@ -125,19 +131,27 @@
updateValues()
userTracker.addCallback(userChangedCallback, backgroundExecutor)
globalSettings.registerContentObserver(deviceProvisionedUri, observer)
+ globalSettings.registerContentObserver(frpActiveUri, observer)
secureSettings.registerContentObserverForUser(userSetupUri, observer, UserHandle.USER_ALL)
}
@WorkerThread
- private fun updateValues(updateDeviceProvisioned: Boolean = true, updateUser: Int = ALL_USERS) {
+ private fun updateValues(
+ updateDeviceProvisioned: Boolean = true,
+ updateFrp: Boolean = true,
+ updateUser: Int = ALL_USERS
+ ) {
if (updateDeviceProvisioned) {
deviceProvisioned
.set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
}
+ if (updateFrp) {
+ frpActive.set(globalSettings.getInt(Settings.Secure.SECURE_FRP_MODE, 0) != 0)
+ }
synchronized(lock) {
if (updateUser == ALL_USERS) {
- val N = userSetupComplete.size()
- for (i in 0 until N) {
+ val n = userSetupComplete.size()
+ for (i in 0 until n) {
val user = userSetupComplete.keyAt(i)
val value = secureSettings
.getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
@@ -172,6 +186,10 @@
return deviceProvisioned.get()
}
+ override fun isFrpActive(): Boolean {
+ return frpActive.get()
+ }
+
override fun isUserSetup(user: Int): Boolean {
val index = synchronized(lock) {
userSetupComplete.indexOfKey(user)
@@ -196,7 +214,13 @@
override fun onDeviceProvisionedChanged() {
dispatchChange(
- DeviceProvisionedController.DeviceProvisionedListener::onDeviceProvisionedChanged
+ DeviceProvisionedController.DeviceProvisionedListener::onDeviceProvisionedChanged
+ )
+ }
+
+ override fun onFrpActiveChanged() {
+ dispatchChange(
+ DeviceProvisionedController.DeviceProvisionedListener::onFrpActiveChanged
)
}
@@ -221,6 +245,7 @@
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("Device provisioned: ${deviceProvisioned.get()}")
+ pw.println("Factory Reset Protection active: ${frpActive.get()}")
synchronized(lock) {
pw.println("User setup complete: $userSetupComplete")
pw.println("Listeners: $listeners")
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index 064c224..c6da55c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -119,11 +119,11 @@
/**
* Default constructor for {@link ObservableServiceConnection}.
- * @param context The context from which the service will be bound with.
+ * @param context The context from which the service will be bound with.
* @param serviceIntent The intent to bind service with.
- * @param executor The executor for connection callbacks to be delivered on
- * @param transformer A {@link ServiceTransformer} for transforming the resulting service
- * into a desired type.
+ * @param executor The executor for connection callbacks to be delivered on
+ * @param transformer A {@link ServiceTransformer} for transforming the resulting service
+ * into a desired type.
*/
@Inject
public ObservableServiceConnection(Context context, Intent serviceIntent,
@@ -143,7 +143,13 @@
* @return {@code true} if initiating binding succeed, {@code false} otherwise.
*/
public boolean bind() {
- final boolean bindResult = mContext.bindService(mServiceIntent, mFlags, mExecutor, this);
+ boolean bindResult = false;
+ try {
+ bindResult = mContext.bindService(mServiceIntent, mFlags, mExecutor, this);
+ } catch (SecurityException e) {
+ Log.d(TAG, "Could not bind to service", e);
+ mContext.unbindService(this);
+ }
mBoundCalled = true;
if (DEBUG) {
Log.d(TAG, "bind. bound:" + bindResult);
@@ -197,7 +203,7 @@
Log.d(TAG, "removeCallback:" + callback);
}
- mExecutor.execute(()-> mCallbacks.removeIf(el -> el.get() == callback));
+ mExecutor.execute(() -> mCallbacks.removeIf(el-> el.get() == callback));
}
private void onDisconnected(@DisconnectReason int reason) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 95cc12a..3c7d092 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -1416,7 +1416,11 @@
@Override
public void onAnimationCancel(@NonNull Animator animation) {
mInteractionJankMonitor.cancel(CUJ_VOLUME_CONTROL);
- Log.d(TAG, "onAnimationCancel");
+ Log.i(TAG, "onAnimationCancel");
+
+ // We can only have one animation listener for cancel, so the jank listener should
+ // also call for cleanup.
+ finishDismiss();
}
@Override
@@ -1525,12 +1529,7 @@
.setDuration(mDialogHideAnimationDurationMs)
.setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
.withEndAction(() -> mHandler.postDelayed(() -> {
- mController.notifyVisible(false);
- mDialog.dismiss();
- tryToRemoveCaptionsTooltip();
- mIsAnimatingDismiss = false;
-
- hideRingerDrawer();
+ finishDismiss();
}, 50));
if (!shouldSlideInVolumeTray()) {
animator.translationX(
@@ -1548,6 +1547,18 @@
Trace.endSection();
}
+ /**
+ * Clean up and hide volume dialog. Called when animation is finished/cancelled.
+ */
+ private void finishDismiss() {
+ mController.notifyVisible(false);
+ mDialog.dismiss();
+ tryToRemoveCaptionsTooltip();
+ mIsAnimatingDismiss = false;
+
+ hideRingerDrawer();
+ }
+
private boolean showActiveStreamOnly() {
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
|| mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index f370ad6..33f0ae5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -36,6 +36,7 @@
import androidx.test.filters.SmallTest;
+import com.android.settingslib.udfps.UdfpsOverlayParams;
import com.android.systemui.doze.util.BurnInHelperKt;
import org.junit.Test;
@@ -107,7 +108,7 @@
Pair<Float, Point> udfps = setupUdfps();
// WHEN udfps location changes
- mAuthControllerCallback.onUdfpsLocationChanged();
+ mAuthControllerCallback.onUdfpsLocationChanged(new UdfpsOverlayParams());
mDelayableExecutor.runAllReady();
// THEN lock icon view location is updated with the same coordinates as auth controller vals
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 52a70ee..47c9191 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -201,13 +201,6 @@
assertThat(magnifierMediumButton.isSelected()).isTrue();
}
- @Test
- public void showSettingPanel_focusOnThePanel() {
- mWindowMagnificationSettings.showSettingPanel();
-
- assertThat(mSettingView.isFocused()).isTrue();
- }
-
private <T extends View> T getInternalView(@IdRes int idRes) {
T view = mSettingView.findViewById(idRes);
assertNotNull(view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 05266f1..9a73898 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -1224,6 +1224,44 @@
}
@Test
+ public void onTouch_WithNewTouchDetection_forwardToKeyguard() throws RemoteException {
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
+ InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData);
+
+ // Enable new touch detection.
+ when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+ // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+ initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false);
+
+ // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDown);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricExecutor.runAllReady();
+
+ // THEN the touch is forwarded to Keyguard
+ verify(mStatusBarKeyguardViewManager).onTouch(downEvent);
+ downEvent.recycle();
+ }
+
+ @Test
public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException {
// GIVEN UDFPS overlay is showing
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
new file mode 100644
index 0000000..87d5ae6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.biometrics.domain.interactor
+
+import android.graphics.Rect
+import android.test.suitebuilder.annotation.SmallTest
+import android.view.MotionEvent
+import android.view.Surface
+import com.android.settingslib.udfps.UdfpsOverlayParams
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(JUnit4::class)
+class UdfpsOverlayInteractorTest : SysuiTestCase() {
+
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ private lateinit var testScope: TestScope
+
+ @Mock private lateinit var authController: AuthController
+ @Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
+
+ @Mock private lateinit var udfpsOverlayParams: UdfpsOverlayParams
+ @Mock private lateinit var overlayBounds: Rect
+
+ private lateinit var underTest: UdfpsOverlayInteractor
+
+ @Before
+ fun setUp() {
+ testScope = TestScope(StandardTestDispatcher())
+ }
+
+ @Test
+ fun testShouldInterceptTouch() =
+ testScope.runTest {
+ createUdpfsOverlayInteractor()
+
+ // When fingerprint enrolled and touch is within bounds
+ verify(authController).addCallback(authControllerCallback.capture())
+ authControllerCallback.value.onUdfpsLocationChanged(udfpsOverlayParams)
+ whenever(authController.isUdfpsEnrolled(anyInt())).thenReturn(true)
+ whenever(udfpsOverlayParams.overlayBounds).thenReturn(overlayBounds)
+ whenever(overlayBounds.contains(downEv.x.toInt(), downEv.y.toInt())).thenReturn(true)
+
+ runCurrent()
+
+ // Then touch should not be intercepted
+ val canInterceptTrue = underTest.canInterceptTouchInUdfpsBounds(downEv)
+ assertThat(canInterceptTrue).isFalse()
+
+ // When touch is outside of bounds
+ whenever(overlayBounds.contains(downEv.x.toInt(), downEv.y.toInt())).thenReturn(false)
+
+ // Then touch should be intercepted
+ val canInterceptFalse = underTest.canInterceptTouchInUdfpsBounds(downEv)
+ assertThat(canInterceptFalse).isTrue()
+ }
+
+ @Test
+ fun testUdfpsOverlayParamsChange() =
+ testScope.runTest {
+ createUdpfsOverlayInteractor()
+ val udfpsOverlayParams = collectLastValue(underTest.udfpsOverlayParams)
+ runCurrent()
+
+ verify(authController).addCallback(authControllerCallback.capture())
+
+ // When udfpsLocationChanges in authcontroller
+ authControllerCallback.value.onUdfpsLocationChanged(firstParams)
+
+ // Then the value in the interactor should be updated
+ assertThat(udfpsOverlayParams()).isEqualTo(firstParams)
+ }
+
+ private fun createUdpfsOverlayInteractor() {
+ underTest = UdfpsOverlayInteractor(authController, testScope.backgroundScope)
+ testScope.runCurrent()
+ }
+}
+
+private val firstParams =
+ UdfpsOverlayParams(Rect(0, 0, 10, 10), Rect(0, 0, 10, 10), 1, 1, 1f, Surface.ROTATION_0)
+private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index 35cd3d2..10bfc1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -45,6 +45,8 @@
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
@@ -62,7 +64,6 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -106,6 +107,7 @@
MockitoAnnotations.initMocks(this)
`when`(userTracker.userId).thenReturn(user)
+ `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
`when`(userTracker.userContext).thenReturn(context)
// Return disabled by default
`when`(packageManager.getComponentEnabledSetting(any()))
@@ -564,6 +566,79 @@
assertTrue(controller.getCurrentServices().isEmpty())
}
+ @Test
+ fun testForceReloadQueriesPackageManager() {
+ val user = 10
+ `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
+
+ controller.forceReload()
+ verify(packageManager).queryIntentServicesAsUser(
+ argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)),
+ argThat(FlagsMatcher(
+ PackageManager.GET_META_DATA.toLong() or
+ PackageManager.GET_SERVICES.toLong() or
+ PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
+ )),
+ eq(UserHandle.of(user))
+ )
+ }
+
+ @Test
+ fun testForceReloadUpdatesList() {
+ val resolveInfo = ResolveInfo()
+ resolveInfo.serviceInfo = ServiceInfo(componentName)
+
+ `when`(packageManager.queryIntentServicesAsUser(
+ argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)),
+ argThat(FlagsMatcher(
+ PackageManager.GET_META_DATA.toLong() or
+ PackageManager.GET_SERVICES.toLong() or
+ PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
+ )),
+ any<UserHandle>()
+ )).thenReturn(listOf(resolveInfo))
+
+ controller.forceReload()
+
+ val services = controller.getCurrentServices()
+ assertThat(services.size).isEqualTo(1)
+ assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
+ }
+
+ @Test
+ fun testForceReloadCallsListeners() {
+ controller.addCallback(mockCallback)
+ executor.runAllReady()
+
+ @Suppress("unchecked_cast")
+ val captor: ArgumentCaptor<List<ControlsServiceInfo>> =
+ ArgumentCaptor.forClass(List::class.java)
+ as ArgumentCaptor<List<ControlsServiceInfo>>
+
+ val resolveInfo = ResolveInfo()
+ resolveInfo.serviceInfo = ServiceInfo(componentName)
+
+ `when`(packageManager.queryIntentServicesAsUser(
+ any(),
+ any<PackageManager.ResolveInfoFlags>(),
+ any<UserHandle>()
+ )).thenReturn(listOf(resolveInfo))
+
+ reset(mockCallback)
+ controller.forceReload()
+
+ verify(mockCallback).onServicesUpdated(capture(captor))
+
+ val services = captor.value
+
+ assertThat(services.size).isEqualTo(1)
+ assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
+ }
+
+
+
private fun ServiceInfo(
componentName: ComponentName,
panelActivityComponentName: ComponentName? = null
@@ -600,7 +675,7 @@
private fun setUpQueryResult(infos: List<ActivityInfo>) {
`when`(
packageManager.queryIntentActivitiesAsUser(
- argThat(IntentMatcher(activityName)),
+ argThat(IntentMatcherComponent(activityName)),
argThat(FlagsMatcher(FLAGS)),
eq(UserHandle.of(user))
)
@@ -609,7 +684,7 @@
})
}
- private class IntentMatcher(
+ private class IntentMatcherComponent(
private val componentName: ComponentName
) : ArgumentMatcher<Intent> {
override fun matches(argument: Intent?): Boolean {
@@ -617,6 +692,14 @@
}
}
+ private class IntentMatcherAction(
+ private val action: String
+ ) : ArgumentMatcher<Intent> {
+ override fun matches(argument: Intent?): Boolean {
+ return argument?.action == action
+ }
+ }
+
private class FlagsMatcher(
private val flags: Long
) : ArgumentMatcher<PackageManager.ResolveInfoFlags> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
index 9d8084d..bd7e98e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
@@ -42,6 +42,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
@@ -80,9 +82,10 @@
fun testNoPreferredPackagesNoDefaultSelected_noNewSelection() {
`when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
- `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+ setUpControlsListingControls(listings)
createStartable(enabled = true).start()
+ fakeExecutor.runAllReady()
verify(controlsController, never()).setPreferredSelection(any())
}
@@ -92,9 +95,10 @@
whenever(authorizedPanelsRepository.getPreferredPackages())
.thenReturn(setOf(TEST_PACKAGE_PANEL))
`when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
- `when`(controlsListingController.getCurrentServices()).thenReturn(emptyList())
+ setUpControlsListingControls(emptyList())
createStartable(enabled = true).start()
+ fakeExecutor.runAllReady()
verify(controlsController, never()).setPreferredSelection(any())
}
@@ -105,9 +109,10 @@
.thenReturn(setOf(TEST_PACKAGE_PANEL))
`when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
val listings = listOf(ControlsServiceInfo(TEST_COMPONENT, "not panel", hasPanel = false))
- `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+ setUpControlsListingControls(listings)
createStartable(enabled = true).start()
+ fakeExecutor.runAllReady()
verify(controlsController, never()).setPreferredSelection(any())
}
@@ -119,9 +124,10 @@
`when`(controlsController.getPreferredSelection())
.thenReturn(mock<SelectedItem.PanelItem>())
val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
- `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+ setUpControlsListingControls(listings)
createStartable(enabled = true).start()
+ fakeExecutor.runAllReady()
verify(controlsController, never()).setPreferredSelection(any())
}
@@ -132,9 +138,10 @@
.thenReturn(setOf(TEST_PACKAGE_PANEL))
`when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
- `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+ setUpControlsListingControls(listings)
createStartable(enabled = true).start()
+ fakeExecutor.runAllReady()
verify(controlsController).setPreferredSelection(listings[0].toPanelItem())
}
@@ -149,9 +156,10 @@
ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true),
ControlsServiceInfo(ComponentName("other_package", "cls"), "non panel", false)
)
- `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+ setUpControlsListingControls(listings)
createStartable(enabled = true).start()
+ fakeExecutor.runAllReady()
verify(controlsController).setPreferredSelection(listings[0].toPanelItem())
}
@@ -166,9 +174,10 @@
ControlsServiceInfo(ComponentName("other_package", "cls"), "panel", true),
ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true)
)
- `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+ setUpControlsListingControls(listings)
createStartable(enabled = true).start()
+ fakeExecutor.runAllReady()
verify(controlsController).setPreferredSelection(listings[1].toPanelItem())
}
@@ -176,10 +185,11 @@
@Test
fun testPreferredSelectionIsPanel_bindOnStart() {
val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
- `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+ setUpControlsListingControls(listings)
`when`(controlsController.getPreferredSelection()).thenReturn(listings[0].toPanelItem())
createStartable(enabled = true).start()
+ fakeExecutor.runAllReady()
verify(controlsController).bindComponentForPanel(TEST_COMPONENT_PANEL)
}
@@ -187,11 +197,12 @@
@Test
fun testPreferredSelectionPanel_listingNoPanel_notBind() {
val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = false))
- `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+ setUpControlsListingControls(listings)
`when`(controlsController.getPreferredSelection())
.thenReturn(SelectedItem.PanelItem("panel", TEST_COMPONENT_PANEL))
createStartable(enabled = true).start()
+ fakeExecutor.runAllReady()
verify(controlsController, never()).bindComponentForPanel(any())
}
@@ -199,10 +210,11 @@
@Test
fun testNotPanelSelection_noBind() {
val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = false))
- `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+ setUpControlsListingControls(listings)
`when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
createStartable(enabled = true).start()
+ fakeExecutor.runAllReady()
verify(controlsController, never()).bindComponentForPanel(any())
}
@@ -221,6 +233,12 @@
verify(controlsController, never()).setPreferredSelection(any())
}
+ private fun setUpControlsListingControls(listings: List<ControlsServiceInfo>) {
+ doAnswer { doReturn(listings).`when`(controlsListingController).getCurrentServices() }
+ .`when`(controlsListingController)
+ .forceReload()
+ }
+
private fun createStartable(enabled: Boolean): ControlsStartable {
val component: ControlsComponent =
mock() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
index 068852d..95c6897 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
@@ -92,9 +92,6 @@
@Captor
private ArgumentCaptor<Observer<Collection<ComplicationViewModel>>> mObserverCaptor;
- @Captor
- private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor;
-
@Complication.Category
static final int COMPLICATION_CATEGORY = Complication.CATEGORY_SYSTEM;
@@ -189,8 +186,6 @@
// Dream entry animations finished.
when(mDreamOverlayStateController.areEntryAnimationsFinished()).thenReturn(true);
- final DreamOverlayStateController.Callback stateCallback = captureOverlayStateCallback();
- stateCallback.onStateChanged();
// Add a complication after entry animations are finished.
final HashSet<ComplicationViewModel> complications = new HashSet<>(
@@ -223,9 +218,4 @@
mObserverCaptor.capture());
return mObserverCaptor.getValue();
}
-
- private DreamOverlayStateController.Callback captureOverlayStateCallback() {
- verify(mDreamOverlayStateController).addCallback(mCallbackCaptor.capture());
- return mCallbackCaptor.getValue();
- }
}
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 fc3a638..5cd24e6 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
@@ -358,6 +358,50 @@
}
@Test
+ fun `LOCKSCREEN to DREAMING`() =
+ testScope.runTest {
+ // GIVEN a device that is not dreaming or dozing
+ keyguardRepository.setDreamingWithOverlay(false)
+ keyguardRepository.setWakefulnessModel(startingToWake())
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ runCurrent()
+
+ // GIVEN a prior transition has run to LOCKSCREEN
+ runner.startTransition(
+ testScope,
+ TransitionInfo(
+ ownerName = "",
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ animator =
+ ValueAnimator().apply {
+ duration = 10
+ interpolator = Interpolators.LINEAR
+ },
+ )
+ )
+ reset(mockTransitionRepository)
+
+ // WHEN the device begins to dream
+ keyguardRepository.setDreamingWithOverlay(true)
+ advanceUntilIdle()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to DREAMING should occur
+ assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(info.to).isEqualTo(KeyguardState.DREAMING)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun `LOCKSCREEN to DOZING`() =
testScope.runTest {
// GIVEN a device with AOD not available
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
index c7f3fa0..fb20bac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
@@ -121,4 +121,92 @@
assertThat(underTest.getName()).doesNotContain("original")
assertThat(underTest.getVal()).isEqualTo("8900")
}
+
+ @Test
+ fun updateTo_emptyToString_isString() {
+ val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+
+ val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+ new.set("newString")
+ underTest.updateTo(new)
+
+ assertThat(underTest.hasData()).isTrue()
+ assertThat(underTest.getName()).contains("newPrefix")
+ assertThat(underTest.getName()).contains("newName")
+ assertThat(underTest.getVal()).isEqualTo("newString")
+ }
+
+ @Test
+ fun updateTo_intToEmpty_isEmpty() {
+ val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+ underTest.set(42)
+
+ val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+ underTest.updateTo(new)
+
+ assertThat(underTest.hasData()).isFalse()
+ assertThat(underTest.getName()).contains("newPrefix")
+ assertThat(underTest.getName()).contains("newName")
+ assertThat(underTest.getVal()).isEqualTo("null")
+ }
+
+ @Test
+ fun updateTo_stringToBool_isBool() {
+ val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+ underTest.set("oldString")
+
+ val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+ new.set(true)
+ underTest.updateTo(new)
+
+ assertThat(underTest.hasData()).isTrue()
+ assertThat(underTest.getName()).contains("newPrefix")
+ assertThat(underTest.getName()).contains("newName")
+ assertThat(underTest.getVal()).isEqualTo("true")
+ }
+
+ @Test
+ fun updateTo_intToString_isString() {
+ val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+ underTest.set(43)
+
+ val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+ new.set("newString")
+ underTest.updateTo(new)
+
+ assertThat(underTest.hasData()).isTrue()
+ assertThat(underTest.getName()).contains("newPrefix")
+ assertThat(underTest.getName()).contains("newName")
+ assertThat(underTest.getVal()).isEqualTo("newString")
+ }
+
+ @Test
+ fun updateTo_boolToInt_isInt() {
+ val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+ underTest.set(false)
+
+ val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+ new.set(44)
+ underTest.updateTo(new)
+
+ assertThat(underTest.hasData()).isTrue()
+ assertThat(underTest.getName()).contains("newPrefix")
+ assertThat(underTest.getName()).contains("newName")
+ assertThat(underTest.getVal()).isEqualTo("44")
+ }
+
+ @Test
+ fun updateTo_boolToNewBool_isNewBool() {
+ val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+ underTest.set(false)
+
+ val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+ new.set(true)
+ underTest.updateTo(new)
+
+ assertThat(underTest.hasData()).isTrue()
+ assertThat(underTest.getName()).contains("newPrefix")
+ assertThat(underTest.getName()).contains("newName")
+ assertThat(underTest.getVal()).isEqualTo("true")
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index 2c8d7ab..949fa1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -435,11 +435,236 @@
assertThat(dumpedString).doesNotContain("testString[0]")
assertThat(dumpedString).doesNotContain("testString[1]")
- assertThat(dumpedString).doesNotContain("testString[2]")
+ // The buffer should contain [MAX_SIZE + 1] entries since we also save the most recently
+ // evicted value.
+ assertThat(dumpedString).contains("testString[2]")
assertThat(dumpedString).contains("testString[3]")
assertThat(dumpedString).contains("testString[${MAX_SIZE + 2}]")
}
+ @Test
+ fun columnEvicted_lastKnownColumnValueInDump() {
+ systemClock.setCurrentTimeMillis(100L)
+ underTest.logChange(prefix = "", columnName = "willBeEvicted", value = "evictedValue")
+
+ // Exactly fill the buffer so that "willBeEvicted" is evicted
+ for (i in 0 until MAX_SIZE) {
+ systemClock.advanceTime(100L)
+ val dumpString = "fillString[$i]"
+ underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+ }
+
+ val dumpedString = dumpChanges()
+
+ // Expect that we'll have both the evicted column entry...
+ val evictedColumnLog =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "willBeEvicted" +
+ SEPARATOR +
+ "evictedValue"
+ assertThat(dumpedString).contains(evictedColumnLog)
+
+ // ... *and* all of the fillingColumn entries.
+ val firstFillingColumnLog =
+ TABLE_LOG_DATE_FORMAT.format(200L) +
+ SEPARATOR +
+ "fillingColumn" +
+ SEPARATOR +
+ "fillString[0]"
+ val lastFillingColumnLog =
+ TABLE_LOG_DATE_FORMAT.format(1100L) +
+ SEPARATOR +
+ "fillingColumn" +
+ SEPARATOR +
+ "fillString[9]"
+ assertThat(dumpedString).contains(firstFillingColumnLog)
+ assertThat(dumpedString).contains(lastFillingColumnLog)
+ }
+
+ @Test
+ fun multipleColumnsEvicted_allColumnsInDump() {
+ systemClock.setCurrentTimeMillis(100L)
+ underTest.logChange(prefix = "", columnName = "willBeEvictedString", value = "evictedValue")
+ systemClock.advanceTime(100L)
+ underTest.logChange(prefix = "", columnName = "willBeEvictedInt", value = 45)
+ systemClock.advanceTime(100L)
+ underTest.logChange(prefix = "", columnName = "willBeEvictedBool", value = true)
+
+ // Exactly fill the buffer so that all the above columns will be evicted
+ for (i in 0 until MAX_SIZE) {
+ systemClock.advanceTime(100L)
+ val dumpString = "fillString[$i]"
+ underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+ }
+
+ val dumpedString = dumpChanges()
+
+ // Expect that we'll have all the evicted column entries...
+ val evictedColumnLogString =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "willBeEvictedString" +
+ SEPARATOR +
+ "evictedValue"
+ val evictedColumnLogInt =
+ TABLE_LOG_DATE_FORMAT.format(200L) + SEPARATOR + "willBeEvictedInt" + SEPARATOR + "45"
+ val evictedColumnLogBool =
+ TABLE_LOG_DATE_FORMAT.format(300L) +
+ SEPARATOR +
+ "willBeEvictedBool" +
+ SEPARATOR +
+ "true"
+ assertThat(dumpedString).contains(evictedColumnLogString)
+ assertThat(dumpedString).contains(evictedColumnLogInt)
+ assertThat(dumpedString).contains(evictedColumnLogBool)
+
+ // ... *and* all of the fillingColumn entries.
+ val firstFillingColumnLog =
+ TABLE_LOG_DATE_FORMAT.format(400) +
+ SEPARATOR +
+ "fillingColumn" +
+ SEPARATOR +
+ "fillString[0]"
+ val lastFillingColumnLog =
+ TABLE_LOG_DATE_FORMAT.format(1300) +
+ SEPARATOR +
+ "fillingColumn" +
+ SEPARATOR +
+ "fillString[9]"
+ assertThat(dumpedString).contains(firstFillingColumnLog)
+ assertThat(dumpedString).contains(lastFillingColumnLog)
+ }
+
+ @Test
+ fun multipleColumnsEvicted_differentPrefixSameName_allColumnsInDump() {
+ systemClock.setCurrentTimeMillis(100L)
+ underTest.logChange(prefix = "prefix1", columnName = "sameName", value = "value1")
+ systemClock.advanceTime(100L)
+ underTest.logChange(prefix = "prefix2", columnName = "sameName", value = "value2")
+ systemClock.advanceTime(100L)
+ underTest.logChange(prefix = "prefix3", columnName = "sameName", value = "value3")
+
+ // Exactly fill the buffer so that all the above columns will be evicted
+ for (i in 0 until MAX_SIZE) {
+ systemClock.advanceTime(100L)
+ val dumpString = "fillString[$i]"
+ underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+ }
+
+ val dumpedString = dumpChanges()
+
+ // Expect that we'll have all the evicted column entries
+ val evictedColumn1 =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "prefix1.sameName" +
+ SEPARATOR +
+ "value1"
+ val evictedColumn2 =
+ TABLE_LOG_DATE_FORMAT.format(200L) +
+ SEPARATOR +
+ "prefix2.sameName" +
+ SEPARATOR +
+ "value2"
+ val evictedColumn3 =
+ TABLE_LOG_DATE_FORMAT.format(300L) +
+ SEPARATOR +
+ "prefix3.sameName" +
+ SEPARATOR +
+ "value3"
+ assertThat(dumpedString).contains(evictedColumn1)
+ assertThat(dumpedString).contains(evictedColumn2)
+ assertThat(dumpedString).contains(evictedColumn3)
+ }
+
+ @Test
+ fun multipleColumnsEvicted_dumpSortedByTimestamp() {
+ systemClock.setCurrentTimeMillis(100L)
+ underTest.logChange(prefix = "", columnName = "willBeEvictedFirst", value = "evictedValue")
+ systemClock.advanceTime(100L)
+ underTest.logChange(prefix = "", columnName = "willBeEvictedSecond", value = 45)
+ systemClock.advanceTime(100L)
+ underTest.logChange(prefix = "", columnName = "willBeEvictedThird", value = true)
+
+ // Exactly fill the buffer with so that all the above columns will be evicted
+ for (i in 0 until MAX_SIZE) {
+ systemClock.advanceTime(100L)
+ val dumpString = "fillString[$i]"
+ underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+ }
+
+ val dumpedString = dumpChanges()
+
+ // Expect that we'll have all the evicted column entries in timestamp order
+ val firstEvictedLog =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "willBeEvictedFirst" +
+ SEPARATOR +
+ "evictedValue"
+ val secondEvictedLog =
+ TABLE_LOG_DATE_FORMAT.format(200L) +
+ SEPARATOR +
+ "willBeEvictedSecond" +
+ SEPARATOR +
+ "45"
+ val thirdEvictedLog =
+ TABLE_LOG_DATE_FORMAT.format(300L) +
+ SEPARATOR +
+ "willBeEvictedThird" +
+ SEPARATOR +
+ "true"
+ assertThat(dumpedString).contains(firstEvictedLog)
+ val stringAfterFirst = dumpedString.substringAfter(firstEvictedLog)
+ assertThat(stringAfterFirst).contains(secondEvictedLog)
+ val stringAfterSecond = stringAfterFirst.substringAfter(secondEvictedLog)
+ assertThat(stringAfterSecond).contains(thirdEvictedLog)
+ }
+
+ @Test
+ fun sameColumnEvictedMultipleTimes_onlyLastEvictionInDump() {
+ systemClock.setCurrentTimeMillis(0L)
+
+ for (i in 1 until 4) {
+ systemClock.advanceTime(100L)
+ val dumpString = "evicted[$i]"
+ underTest.logChange(prefix = "", columnName = "evictedColumn", value = dumpString)
+ }
+
+ // Exactly fill the buffer so that all the entries for "evictedColumn" will be evicted.
+ for (i in 0 until MAX_SIZE) {
+ systemClock.advanceTime(100L)
+ val dumpString = "fillString[$i]"
+ underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+ }
+
+ val dumpedString = dumpChanges()
+
+ // Expect that we only have the most recent evicted column entry
+ val evictedColumnLog1 =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "evictedColumn" +
+ SEPARATOR +
+ "evicted[1]"
+ val evictedColumnLog2 =
+ TABLE_LOG_DATE_FORMAT.format(200L) +
+ SEPARATOR +
+ "evictedColumn" +
+ SEPARATOR +
+ "evicted[2]"
+ val evictedColumnLog3 =
+ TABLE_LOG_DATE_FORMAT.format(300L) +
+ SEPARATOR +
+ "evictedColumn" +
+ SEPARATOR +
+ "evicted[3]"
+ assertThat(dumpedString).doesNotContain(evictedColumnLog1)
+ assertThat(dumpedString).doesNotContain(evictedColumnLog2)
+ assertThat(dumpedString).contains(evictedColumnLog3)
+ }
+
private fun dumpChanges(): String {
underTest.dump(PrintWriter(outputWriter), arrayOf())
return outputWriter.toString()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 608d809..3f940d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -72,7 +72,7 @@
MockitoAnnotations.initMocks(this)
whenever(context.packageManager).thenReturn(packageManager)
- whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(noteTaskInfo)
+ whenever(resolver.resolveInfo(any(), any())).thenReturn(noteTaskInfo)
whenever(userManager.isUserUnlocked).thenReturn(true)
whenever(
devicePolicyManager.getKeyguardDisabledFeatures(
@@ -102,7 +102,7 @@
// region onBubbleExpandChanged
@Test
fun onBubbleExpandChanged_expanding_logNoteTaskOpened() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false)
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false)
createNoteTaskController()
.apply { infoReference.set(expectedInfo) }
@@ -117,7 +117,7 @@
@Test
fun onBubbleExpandChanged_collapsing_logNoteTaskClosed() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false)
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false)
createNoteTaskController()
.apply { infoReference.set(expectedInfo) }
@@ -132,7 +132,7 @@
@Test
fun onBubbleExpandChanged_expandingAndKeyguardLocked_doNothing() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false)
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true)
createNoteTaskController()
.apply { infoReference.set(expectedInfo) }
@@ -146,35 +146,7 @@
@Test
fun onBubbleExpandChanged_notExpandingAndKeyguardLocked_doNothing() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false)
-
- createNoteTaskController()
- .apply { infoReference.set(expectedInfo) }
- .onBubbleExpandChanged(
- isExpanding = false,
- key = Bubble.KEY_APP_BUBBLE,
- )
-
- verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
- }
-
- @Test
- fun onBubbleExpandChanged_expandingAndInMultiWindowMode_doNothing() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = true)
-
- createNoteTaskController()
- .apply { infoReference.set(expectedInfo) }
- .onBubbleExpandChanged(
- isExpanding = true,
- key = Bubble.KEY_APP_BUBBLE,
- )
-
- verifyZeroInteractions(context, bubbles, keyguardManager, userManager)
- }
-
- @Test
- fun onBubbleExpandChanged_notExpandingAndInMultiWindowMode_doNothing() {
- val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = true)
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true)
createNoteTaskController()
.apply { infoReference.set(expectedInfo) }
@@ -215,16 +187,14 @@
val expectedInfo =
noteTaskInfo.copy(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
isKeyguardLocked = true,
)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
- whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
+ whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
.showNoteTask(
entryPoint = expectedInfo.entryPoint!!,
- isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
)
val intentCaptor = argumentCaptor<Intent>()
@@ -250,16 +220,14 @@
val expectedInfo =
noteTaskInfo.copy(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
isKeyguardLocked = false,
)
- whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
+ whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
createNoteTaskController()
.showNoteTask(
entryPoint = expectedInfo.entryPoint!!,
- isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
)
verifyZeroInteractions(context)
@@ -275,49 +243,10 @@
}
@Test
- fun showNoteTask_isInMultiWindowMode_shouldStartActivityAndLogUiEvent() {
- val expectedInfo =
- noteTaskInfo.copy(
- entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT,
- isInMultiWindowMode = true,
- isKeyguardLocked = false,
- )
- whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
- whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
-
- createNoteTaskController()
- .showNoteTask(
- entryPoint = expectedInfo.entryPoint!!,
- isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
- )
-
- val intentCaptor = argumentCaptor<Intent>()
- val userCaptor = argumentCaptor<UserHandle>()
- verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
-
- (intentCaptor.value.flags and FLAG_ACTIVITY_NEW_TASK) == FLAG_ACTIVITY_NEW_TASK
-
- intentCaptor.value.let { intent ->
- assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
- assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
- assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
- .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
- .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
- assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
- }
- assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
- verify(eventLogger).logNoteTaskOpened(expectedInfo)
- verifyZeroInteractions(bubbles)
- }
-
- @Test
fun showNoteTask_bubblesIsNull_shouldDoNothing() {
createNoteTaskController(bubbles = null)
.showNoteTask(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
)
verifyZeroInteractions(context, bubbles, eventLogger)
@@ -325,12 +254,11 @@
@Test
fun showNoteTask_intentResolverReturnsNull_shouldDoNothing() {
- whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(null)
+ whenever(resolver.resolveInfo(any(), any())).thenReturn(null)
createNoteTaskController()
.showNoteTask(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
)
verifyZeroInteractions(context, bubbles, eventLogger)
@@ -341,7 +269,6 @@
createNoteTaskController(isEnabled = false)
.showNoteTask(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
)
verifyZeroInteractions(context, bubbles, eventLogger)
@@ -354,7 +281,6 @@
createNoteTaskController()
.showNoteTask(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isInMultiWindowMode = false,
)
verifyZeroInteractions(context, bubbles, eventLogger)
@@ -405,11 +331,7 @@
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
- createNoteTaskController()
- .showNoteTask(
- isInMultiWindowMode = false,
- entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
- )
+ createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -425,11 +347,7 @@
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
- createNoteTaskController()
- .showNoteTask(
- isInMultiWindowMode = false,
- entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
- )
+ createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -445,11 +363,7 @@
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
- createNoteTaskController()
- .showNoteTask(
- isInMultiWindowMode = false,
- entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
- )
+ createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
val intentCaptor = argumentCaptor<Intent>()
verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
@@ -472,11 +386,7 @@
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
- createNoteTaskController()
- .showNoteTask(
- isInMultiWindowMode = false,
- entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
- )
+ createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
val intentCaptor = argumentCaptor<Intent>()
verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
index 7e975b6..91cd6ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
@@ -31,41 +31,19 @@
NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
@Test
- fun launchMode_notInMultiWindowModeAndKeyguardUnlocked_launchModeAppBubble() {
- val underTest =
- createNoteTaskInfo()
- .copy(
- isKeyguardLocked = false,
- isInMultiWindowMode = false,
- )
+ fun launchMode_keyguardLocked_launchModeActivity() {
+ val underTest = createNoteTaskInfo().copy(isKeyguardLocked = true)
+
+ assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
+ }
+
+ @Test
+ fun launchMode_keyguardUnlocked_launchModeActivity() {
+ val underTest = createNoteTaskInfo().copy(isKeyguardLocked = false)
assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble)
}
- @Test
- fun launchMode_inMultiWindowMode_launchModeActivity() {
- val underTest =
- createNoteTaskInfo()
- .copy(
- isKeyguardLocked = false,
- isInMultiWindowMode = true,
- )
-
- assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
- }
-
- @Test
- fun launchMode_keyguardLocked_launchModeActivity() {
- val underTest =
- createNoteTaskInfo()
- .copy(
- isKeyguardLocked = true,
- isInMultiWindowMode = false,
- )
-
- assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
- }
-
private companion object {
const val NOTES_PACKAGE_NAME = "com.android.note.app"
const val NOTES_UID = 123456
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index e57d0d9..d44012f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -18,75 +18,139 @@
package com.android.systemui.notetask.quickaffordance
+import android.hardware.input.InputSettings
import android.test.suitebuilder.annotation.SmallTest
-import androidx.test.runner.AndroidJUnit4
+import android.testing.AndroidTestingRunner
+import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
+import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.stylus.StylusManager
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
-/**
- * Tests for [NoteTaskQuickAffordanceConfig].
- *
- * Build/Install/Run:
- * - atest SystemUITests:NoteTaskQuickAffordanceConfigTest
- */
+/** atest SystemUITests:NoteTaskQuickAffordanceConfigTest */
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(AndroidTestingRunner::class)
internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
- @Mock lateinit var noteTaskController: NoteTaskController
+ @Mock lateinit var controller: NoteTaskController
+ @Mock lateinit var stylusManager: StylusManager
+ @Mock lateinit var repository: KeyguardQuickAffordanceRepository
+
+ private lateinit var mockitoSession: MockitoSession
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .mockStatic(InputSettings::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+
+ whenever(InputSettings.isStylusEverUsed(mContext)).then { true }
+ whenever(repository.selections).then {
+ val map = mapOf("" to listOf(createUnderTest()))
+ MutableStateFlow(map)
+ }
}
- private fun createUnderTest(isEnabled: Boolean) =
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+ }
+
+ private fun createUnderTest(isEnabled: Boolean = true): KeyguardQuickAffordanceConfig =
NoteTaskQuickAffordanceConfig(
context = context,
- noteTaskController = noteTaskController,
+ controller = controller,
+ stylusManager = stylusManager,
+ lazyRepository = { repository },
isEnabled = isEnabled,
)
+ private fun createLockScreenStateVisible(): LockScreenState =
+ LockScreenState.Visible(
+ icon =
+ Icon.Resource(
+ res = R.drawable.ic_note_task_shortcut_keyguard,
+ contentDescription =
+ ContentDescription.Resource(R.string.note_task_button_label),
+ )
+ )
+
+ @Test
+ fun lockScreenState_stylusUsed_noCustomShortcutSelected_shouldEmitVisible() = runTest {
+ val underTest = createUnderTest()
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(createLockScreenStateVisible())
+ }
+
+ @Test
+ fun lockScreenState_noStylusEverUsed_noCustomShortcutSelected_shouldEmitVisible() = runTest {
+ whenever(InputSettings.isStylusEverUsed(mContext)).then { false }
+ val underTest = createUnderTest()
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(createLockScreenStateVisible())
+ }
+
+ @Test
+ fun lockScreenState_stylusUsed_customShortcutSelected_shouldEmitVisible() = runTest {
+ whenever(repository.selections).then {
+ val map = mapOf<String, List<KeyguardQuickAffordanceConfig>>()
+ MutableStateFlow(map)
+ }
+ val underTest = createUnderTest()
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(createLockScreenStateVisible())
+ }
+
+ @Test
+ fun lockScreenState_noIsStylusEverUsed_noCustomShortcutSelected_shouldEmitHidden() = runTest {
+ whenever(InputSettings.isStylusEverUsed(mContext)).then { false }
+ whenever(repository.selections).then {
+ val map = mapOf<String, List<KeyguardQuickAffordanceConfig>>()
+ MutableStateFlow(map)
+ }
+ val underTest = createUnderTest()
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+
@Test
fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest {
val underTest = createUnderTest(isEnabled = false)
- val actual = collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual()).isEqualTo(LockScreenState.Hidden)
- }
-
- @Test
- fun lockScreenState_isEnabled_shouldEmitVisible() = runTest {
- val stringResult = "Notetaking"
- val underTest = createUnderTest(isEnabled = true)
-
- val actual = collectLastValue(underTest.lockScreenState)
-
- val expected =
- LockScreenState.Visible(
- icon =
- Icon.Resource(
- res = R.drawable.ic_note_task_shortcut_keyguard,
- contentDescription = ContentDescription.Loaded(stringResult),
- )
- )
- assertThat(actual()).isEqualTo(expected)
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
}
@Test
@@ -95,6 +159,6 @@
underTest.onTriggered(expandable = null)
- verify(noteTaskController).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
new file mode 100644
index 0000000..eb7b481
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -0,0 +1,186 @@
+/*
+ * 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.recents
+
+import android.content.ComponentName
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.app.AssistUtils
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.model.SysUiState
+import com.android.systemui.navigationbar.NavigationBarController
+import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP
+import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.recents.IOverviewProxy
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_OFF
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_ON
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_OFF
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_ON
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_STATE_MASK
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
+import com.android.systemui.util.mockito.whenever
+import com.android.wm.shell.sysui.ShellInterface
+import com.google.common.util.concurrent.MoreExecutors
+import dagger.Lazy
+import java.util.Optional
+import java.util.concurrent.Executor
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.intThat
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class OverviewProxyServiceTest : SysuiTestCase() {
+
+ @Main private val executor: Executor = MoreExecutors.directExecutor()
+
+ private lateinit var subject: OverviewProxyService
+ private val dumpManager = DumpManager()
+ private val displayTracker = FakeDisplayTracker(mContext)
+ private val sysUiState = SysUiState(displayTracker)
+ private val screenLifecycle = ScreenLifecycle(dumpManager)
+
+ @Mock private lateinit var overviewProxy: IOverviewProxy.Stub
+ @Mock private lateinit var packageManager: PackageManager
+
+ // The following mocks belong to not-yet-tested parts of OverviewProxyService.
+ @Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var shellInterface: ShellInterface
+ @Mock private lateinit var navBarController: NavigationBarController
+ @Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var navModeController: NavigationModeController
+ @Mock private lateinit var statusBarWinController: NotificationShadeWindowController
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var sysuiUnlockAnimationController: KeyguardUnlockAnimationController
+ @Mock private lateinit var assistUtils: AssistUtils
+ @Mock
+ private lateinit var unfoldTransitionProgressForwarder:
+ Optional<UnfoldTransitionProgressForwarder>
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ val serviceComponent = ComponentName("test_package", "service_provider")
+ context.addMockService(serviceComponent, overviewProxy)
+ context.addMockServiceResolver(
+ TestableContext.MockServiceResolver {
+ if (it.action == ACTION_QUICKSTEP) serviceComponent else null
+ }
+ )
+ whenever(overviewProxy.queryLocalInterface(ArgumentMatchers.anyString()))
+ .thenReturn(overviewProxy)
+ whenever(overviewProxy.asBinder()).thenReturn(overviewProxy)
+
+ // packageManager.resolveServiceAsUser has to return non-null for
+ // OverviewProxyService#isEnabled to become true.
+ context.setMockPackageManager(packageManager)
+ whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(mock(ResolveInfo::class.java))
+
+ subject =
+ OverviewProxyService(
+ context,
+ executor,
+ commandQueue,
+ shellInterface,
+ Lazy { navBarController },
+ Lazy { Optional.of(centralSurfaces) },
+ navModeController,
+ statusBarWinController,
+ sysUiState,
+ userTracker,
+ screenLifecycle,
+ uiEventLogger,
+ displayTracker,
+ sysuiUnlockAnimationController,
+ assistUtils,
+ dumpManager,
+ unfoldTransitionProgressForwarder
+ )
+ }
+
+ @After
+ fun tearDown() {
+ subject.shutdownForTest()
+ }
+
+ @Test
+ fun `ScreenLifecycle - screenTurnedOn triggers SysUI state flag changes `() {
+ screenLifecycle.dispatchScreenTurnedOn()
+
+ verify(overviewProxy)
+ .onSystemUiStateChanged(
+ intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_ON }
+ )
+ }
+
+ @Test
+ fun `ScreenLifecycle - screenTurningOn triggers SysUI state flag changes `() {
+ screenLifecycle.dispatchScreenTurningOn()
+
+ verify(overviewProxy)
+ .onSystemUiStateChanged(
+ intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_ON }
+ )
+ }
+
+ @Test
+ fun `ScreenLifecycle - screenTurnedOff triggers SysUI state flag changes `() {
+ screenLifecycle.dispatchScreenTurnedOff()
+
+ verify(overviewProxy)
+ .onSystemUiStateChanged(
+ intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_OFF }
+ )
+ }
+
+ @Test
+ fun `ScreenLifecycle - screenTurningOff triggers SysUI state flag changes `() {
+ screenLifecycle.dispatchScreenTurningOff()
+
+ verify(overviewProxy)
+ .onSystemUiStateChanged(
+ intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_OFF }
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 82a5743..51492eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -26,6 +26,7 @@
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
@@ -86,6 +87,7 @@
@Mock private lateinit var pulsingGestureListener: PulsingGestureListener
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
@Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ @Mock private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@@ -133,6 +135,7 @@
keyguardBouncerViewModel,
keyguardBouncerComponentFactory,
alternateBouncerInteractor,
+ udfpsOverlayInteractor,
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
)
@@ -265,6 +268,17 @@
}
@Test
+ fun shouldInterceptTouchEvent_downEventAlternateBouncer_ignoreIfInUdfpsOverlay() {
+ // Down event within udfpsOverlay bounds while alternateBouncer is showing
+ whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(downEv)).thenReturn(false)
+ whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
+
+ // Then touch should not be intercepted
+ val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(downEv)
+ assertThat(shouldIntercept).isFalse()
+ }
+
+ @Test
fun testGetBouncerContainer() {
Mockito.clearInvocations(view)
underTest.bouncerContainer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index faa6221..2f528a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -40,6 +40,7 @@
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -101,6 +102,7 @@
@Mock private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@Mock private NotificationInsetsController mNotificationInsetsController;
@Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
+ @Mock private UdfpsOverlayInteractor mUdfpsOverlayInteractor;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
@@ -152,6 +154,7 @@
mKeyguardBouncerViewModel,
mKeyguardBouncerComponentFactory,
mAlternateBouncerInteractor,
+ mUdfpsOverlayInteractor,
mKeyguardTransitionInteractor,
mPrimaryBouncerToGoneTransitionViewModel
);
@@ -177,6 +180,7 @@
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+ when(mUdfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(any())).thenReturn(true);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we should intercept touch
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index e3a3678..15b8423 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -32,6 +32,7 @@
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -77,7 +78,6 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
@@ -91,9 +91,12 @@
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.List;
+
import dagger.Lazy;
@SmallTest
@@ -101,12 +104,14 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class QuickSettingsControllerTest extends SysuiTestCase {
- private static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400;
private static final float QS_FRAME_START_X = 0f;
private static final int QS_FRAME_WIDTH = 1000;
private static final int QS_FRAME_TOP = 0;
private static final int QS_FRAME_BOTTOM = 1000;
-
+ private static final int DEFAULT_HEIGHT = 1000;
+ // In split shade min = max
+ private static final int DEFAULT_MIN_HEIGHT_SPLIT_SHADE = DEFAULT_HEIGHT;
+ private static final int DEFAULT_MIN_HEIGHT = 300;
private QuickSettingsController mQsController;
@@ -115,7 +120,6 @@
@Mock private KeyguardStatusBarView mKeyguardStatusBar;
@Mock private QS mQs;
@Mock private QSFragment mQSFragment;
-
@Mock private Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationPanelView mPanelView;
@@ -147,10 +151,7 @@
@Mock private FeatureFlags mFeatureFlags;
@Mock private InteractionJankMonitor mInteractionJankMonitor;
@Mock private ShadeLogger mShadeLogger;
-
@Mock private DumpManager mDumpManager;
-
- @Mock private HeadsUpManagerPhone mHeadsUpManager;
@Mock private UiEventLogger mUiEventLogger;
private SysuiStatusBarStateController mStatusBarStateController;
@@ -173,6 +174,8 @@
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
+ when(mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_qs_transition_distance)).thenReturn(DEFAULT_HEIGHT);
when(mPanelView.getResources()).thenReturn(mResources);
when(mPanelView.getContext()).thenReturn(getContext());
when(mPanelView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
@@ -529,6 +532,88 @@
assertThat(mQsController.isOpenQsEvent(event)).isTrue();
}
+ @Test
+ public void shadeClosed_onLockscreen_inSplitShade_setsQsNotVisible() {
+ mQsController.setQs(mQs);
+ enableSplitShade(true);
+ lockScreen();
+
+ closeLockedQS();
+
+ assertQsVisible(false);
+ }
+
+ @Test
+ public void shadeOpened_onLockscreen_inSplitShade_setsQsVisible() {
+ mQsController.setQs(mQs);
+ enableSplitShade(true);
+ lockScreen();
+
+ openLockedQS();
+
+ assertQsVisible(true);
+ }
+
+ @Test
+ public void shadeClosed_onLockscreen_inSingleShade_setsQsNotVisible() {
+ mQsController.setQs(mQs);
+ enableSplitShade(false);
+ lockScreen();
+
+ closeLockedQS();
+
+ verify(mQs).setQsVisible(false);
+ }
+
+ @Test
+ public void shadeOpened_onLockscreen_inSingleShade_setsQsVisible() {
+ mQsController.setQs(mQs);
+ enableSplitShade(false);
+ lockScreen();
+
+ openLockedQS();
+
+ verify(mQs).setQsVisible(true);
+ }
+
+ private void lockScreen() {
+ mQsController.setBarState(KEYGUARD);
+ }
+
+ private void openLockedQS() {
+ when(mLockscreenShadeTransitionController.getQSDragProgress())
+ .thenReturn((float) DEFAULT_HEIGHT);
+ mLockscreenShadeTransitionCallback.setTransitionToFullShadeAmount(
+ /* pxAmount= */ DEFAULT_HEIGHT,
+ /* animate=*/ false,
+ /* delay= */ 0
+ );
+ }
+
+ private void closeLockedQS() {
+ when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(0f);
+ mLockscreenShadeTransitionCallback.setTransitionToFullShadeAmount(
+ /* pxAmount= */ 0,
+ /* animate=*/ false,
+ /* delay= */ 0
+ );
+ }
+
+ private void setSplitShadeHeightProperties() {
+ // In split shade, min = max
+ when(mQs.getQsMinExpansionHeight()).thenReturn(DEFAULT_MIN_HEIGHT_SPLIT_SHADE);
+ when(mQs.getDesiredHeight()).thenReturn(DEFAULT_HEIGHT);
+ mQsController.updateMinHeight();
+ mQsController.onHeightChanged();
+ }
+
+ private void setDefaultHeightProperties() {
+ when(mQs.getQsMinExpansionHeight()).thenReturn(DEFAULT_MIN_HEIGHT);
+ when(mQs.getDesiredHeight()).thenReturn(DEFAULT_HEIGHT);
+ mQsController.updateMinHeight();
+ mQsController.onHeightChanged();
+ }
+
private static MotionEvent createMotionEvent(int x, int y, int action) {
return MotionEvent.obtain(0, 0, action, x, y, 0);
}
@@ -549,6 +634,11 @@
private void enableSplitShade(boolean enabled) {
when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled);
mQsController.updateResources();
+ if (enabled) {
+ setSplitShadeHeightProperties();
+ } else {
+ setDefaultHeightProperties();
+ }
}
private void setIsFullWidth(boolean fullWidth) {
@@ -561,5 +651,11 @@
mQsController.handleShadeLayoutChanged(oldMaxHeight);
}
-
+ private void assertQsVisible(boolean visible) {
+ ArgumentCaptor<Boolean> visibilityCaptor = ArgumentCaptor.forClass(Boolean.class);
+ verify(mQs, atLeastOnce()).setQsVisible(visibilityCaptor.capture());
+ List<Boolean> allVisibilities = visibilityCaptor.getAllValues();
+ boolean lastVisibility = allVisibilities.get(allVisibilities.size() - 1);
+ assertThat(lastVisibility).isEqualTo(visible);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index c4ee326..031c17f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -1091,6 +1091,34 @@
}
@Test
+ public void testOccludingQSNotExpanded_transitionToAuthScrimmed() {
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+ // GIVEN device occluded and panel is NOT expanded
+ mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
+ when(mKeyguardStateController.isOccluded()).thenReturn(true);
+ mCentralSurfaces.mPanelExpanded = false;
+
+ mCentralSurfaces.updateScrimController();
+
+ verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED));
+ }
+
+ @Test
+ public void testOccludingQSExpanded_transitionToAuthScrimmedShade() {
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+ // GIVEN device occluded and qs IS expanded
+ mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
+ when(mKeyguardStateController.isOccluded()).thenReturn(true);
+ mCentralSurfaces.mPanelExpanded = true;
+
+ mCentralSurfaces.updateScrimController();
+
+ verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
+ }
+
+ @Test
public void testShowKeyguardImplementation_setsState() {
when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
@@ -1314,6 +1342,15 @@
verify(mPowerManagerService, never()).wakeUp(anyLong(), anyInt(), anyString(), anyString());
}
+ @Test
+ public void frpLockedDevice_shadeDisabled() {
+ when(mDeviceProvisionedController.isFrpActive()).thenReturn(true);
+ when(mDozeServiceHost.isPulsing()).thenReturn(true);
+ mCentralSurfaces.updateNotificationPanelTouchState();
+
+ verify(mNotificationPanelViewController).setTouchAndAnimationDisabled(true);
+ }
+
/**
* Configures the appropriate mocks and then calls {@link CentralSurfacesImpl#updateIsKeyguard}
* to reconfigure the keyguard to reflect the requested showing/occluded states.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 158e9ad..e2019b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -683,4 +683,30 @@
// the following call before registering centralSurfaces should NOT throw a NPE:
mStatusBarKeyguardViewManager.hideAlternateBouncer(true);
}
+
+ @Test
+ public void testResetHideBouncerWhenShowing_alternateBouncerHides() {
+ // GIVEN the keyguard is showing
+ reset(mAlternateBouncerInteractor);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+
+ // WHEN SBKV is reset with hideBouncerWhenShowing=true
+ mStatusBarKeyguardViewManager.reset(true);
+
+ // THEN alternate bouncer is hidden
+ verify(mAlternateBouncerInteractor).hide();
+ }
+
+ @Test
+ public void testResetHideBouncerWhenShowingIsFalse_alternateBouncerHides() {
+ // GIVEN the keyguard is showing
+ reset(mAlternateBouncerInteractor);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+
+ // WHEN SBKV is reset with hideBouncerWhenShowing=false
+ mStatusBarKeyguardViewManager.reset(false);
+
+ // THEN alternate bouncer is NOT hidden
+ verify(mAlternateBouncerInteractor, never()).hide();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
index 5129f85..6980a0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
@@ -90,6 +90,12 @@
}
@Test
+ fun testFrpNotActiveByDefault() {
+ init()
+ assertThat(controller.isFrpActive).isFalse()
+ }
+
+ @Test
fun testNotUserSetupByDefault() {
init()
assertThat(controller.isUserSetup(START_USER)).isFalse()
@@ -104,6 +110,14 @@
}
@Test
+ fun testFrpActiveWhenCreated() {
+ settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+ init()
+
+ assertThat(controller.isFrpActive).isTrue()
+ }
+
+ @Test
fun testUserSetupWhenCreated() {
settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
init()
@@ -122,6 +136,16 @@
}
@Test
+ fun testFrpActiveChange() {
+ init()
+
+ settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+ testableLooper.processAllMessages() // background observer
+
+ assertThat(controller.isFrpActive).isTrue()
+ }
+
+ @Test
fun testUserSetupChange() {
init()
@@ -164,6 +188,7 @@
mainExecutor.runAllReady()
verify(listener, never()).onDeviceProvisionedChanged()
+ verify(listener, never()).onFrpActiveChanged()
verify(listener, never()).onUserSetupChanged()
verify(listener, never()).onUserSwitched()
}
@@ -181,6 +206,7 @@
verify(listener).onUserSwitched()
verify(listener, never()).onUserSetupChanged()
verify(listener, never()).onDeviceProvisionedChanged()
+ verify(listener, never()).onFrpActiveChanged()
}
@Test
@@ -195,6 +221,7 @@
verify(listener, never()).onUserSwitched()
verify(listener).onUserSetupChanged()
verify(listener, never()).onDeviceProvisionedChanged()
+ verify(listener, never()).onFrpActiveChanged()
}
@Test
@@ -208,10 +235,26 @@
verify(listener, never()).onUserSwitched()
verify(listener, never()).onUserSetupChanged()
+ verify(listener, never()).onFrpActiveChanged()
verify(listener).onDeviceProvisionedChanged()
}
@Test
+ fun testListenerCalledOnFrpActiveChanged() {
+ init()
+ controller.addCallback(listener)
+
+ settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+ testableLooper.processAllMessages()
+ mainExecutor.runAllReady()
+
+ verify(listener, never()).onUserSwitched()
+ verify(listener, never()).onUserSetupChanged()
+ verify(listener, never()).onDeviceProvisionedChanged()
+ verify(listener).onFrpActiveChanged()
+ }
+
+ @Test
fun testRemoveListener() {
init()
controller.addCallback(listener)
@@ -220,11 +263,13 @@
switchUser(10)
settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+ settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
testableLooper.processAllMessages()
mainExecutor.runAllReady()
verify(listener, never()).onDeviceProvisionedChanged()
+ verify(listener, never()).onFrpActiveChanged()
verify(listener, never()).onUserSetupChanged()
verify(listener, never()).onUserSwitched()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index 046ad12..f9bfafc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.util.service;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -169,4 +171,19 @@
verify(mCallback).onDisconnected(eq(connection),
eq(ObservableServiceConnection.DISCONNECT_REASON_UNBIND));
}
+
+ @Test
+ public void testBindServiceThrowsError() {
+ ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
+ mIntent, mExecutor, mTransformer);
+ connection.addCallback(mCallback);
+
+ when(mContext.bindService(eq(mIntent), anyInt(), eq(mExecutor), eq(connection)))
+ .thenThrow(new SecurityException());
+
+ // Verify that the exception was caught and that bind returns false, and we properly
+ // unbind.
+ assertThat(connection.bind()).isFalse();
+ verify(mContext).unbindService(connection);
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index b54dbbf..7f6ad43 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -742,6 +742,9 @@
return Collections.EMPTY_LIST;
}
final String typeHints = mService.getMaster().getPccProviderHints();
+ if (sVerbose) {
+ Slog.v(TAG, "TypeHints flag:" + typeHints);
+ }
if (TextUtils.isEmpty(typeHints)) {
return new ArrayList<>();
}
@@ -757,7 +760,7 @@
@GuardedBy("mLock")
void maybeRequestFieldClassificationFromServiceLocked() {
if (mClassificationState.mPendingFieldClassificationRequest == null) {
- Log.w(TAG, "Received AssistData without pending classification request");
+ Slog.w(TAG, "Received AssistData without pending classification request");
return;
}
@@ -791,7 +794,8 @@
final int requestId = receiverExtras.getInt(EXTRA_REQUEST_ID);
if (sVerbose) {
- Slog.v(TAG, "New structure for requestId " + requestId + ": " + structure);
+ Slog.v(TAG, "New structure for PCC Detection: requestId " + requestId + ": "
+ + structure);
}
synchronized (mLock) {
@@ -1125,6 +1129,13 @@
// structure is taken. This causes only one fill request per burst of focus changes.
cancelCurrentRequestLocked();
+ if (mClassificationState.mHintsToAutofillIdMap == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "triggering field classification");
+ }
+ requestAssistStructureForPccLocked(flags | FLAG_PCC_DETECTION);
+ }
+
// Only ask IME to create inline suggestions request when
// 1. Autofill provider supports it or client enabled client suggestions.
// 2. The render service is available.
@@ -1376,7 +1387,6 @@
@Override
public void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
@NonNull String servicePackageName, int requestFlags) {
-
final AutofillId[] fieldClassificationIds;
final LogMaker requestLog;
@@ -1609,10 +1619,68 @@
Set<Dataset> eligibleDatasets = new ArraySet<>();
Set<AutofillId> eligibleAutofillIds = new ArraySet<>();
for (Dataset dataset : response.getDatasets()) {
- if (dataset.getFieldIds() == null) continue;
+ if (dataset.getFieldIds() == null || dataset.getFieldIds().isEmpty()) continue;
if (dataset.getAutofillDatatypes() != null
- && dataset.getAutofillDatatypes().size() > 0) {
- continue;
+ && !dataset.getAutofillDatatypes().isEmpty()) {
+ // This dataset has information relevant for detection too, so we should filter
+ // them out. It's possible that some fields are applicable to hints only, as such,
+ // they need to be filtered off.
+ // TODO(b/266379948): Verify the logic and add tests
+ // Update dataset to only have non-null fieldValues
+
+ // Figure out if we need to process results.
+ boolean conversionRequired = false;
+ int newSize = dataset.getFieldIds().size();
+ for (AutofillId id : dataset.getFieldIds()) {
+ if (id == null) {
+ conversionRequired = true;
+ newSize--;
+ }
+ }
+
+ if (conversionRequired) {
+ ArrayList<AutofillId> fieldIds = new ArrayList<>(newSize);
+ ArrayList<AutofillValue> fieldValues = new ArrayList<>(newSize);
+ ArrayList<RemoteViews> fieldPresentations = new ArrayList<>(newSize);
+ ArrayList<RemoteViews> fieldDialogPresentations = new ArrayList<>(newSize);
+ ArrayList<InlinePresentation> fieldInlinePresentations =
+ new ArrayList<>(newSize);
+ ArrayList<InlinePresentation> fieldInlineTooltipPresentations =
+ new ArrayList<>(newSize);
+ ArrayList<Dataset.DatasetFieldFilter> fieldFilters = new ArrayList<>(newSize);
+
+ for (int i = 0; i < dataset.getFieldIds().size(); i++) {
+ AutofillId id = dataset.getFieldIds().get(i);
+ if (id != null) {
+ // Copy over
+ fieldIds.add(id);
+ fieldValues.add(dataset.getFieldValues().get(i));
+ fieldPresentations.add(dataset.getFieldPresentation(i));
+ fieldDialogPresentations.add(dataset.getFieldDialogPresentation(i));
+ fieldInlinePresentations.add(dataset.getFieldInlinePresentation(i));
+ fieldInlineTooltipPresentations.add(
+ dataset.getFieldInlineTooltipPresentation(i));
+ fieldFilters.add(dataset.getFilter(i));
+ }
+ }
+ dataset =
+ new Dataset(
+ fieldIds,
+ fieldValues,
+ fieldPresentations,
+ fieldDialogPresentations,
+ fieldInlinePresentations,
+ fieldInlineTooltipPresentations,
+ fieldFilters,
+ new ArrayList<>(),
+ dataset.getFieldContent(),
+ null,
+ null,
+ null,
+ null,
+ dataset.getId(),
+ dataset.getAuthentication());
+ }
}
eligibleDatasets.add(dataset);
for (AutofillId id : dataset.getFieldIds()) {
@@ -1639,6 +1707,7 @@
ArrayMap<String, Set<AutofillId>> hintsToAutofillIdMap =
mClassificationState.mHintsToAutofillIdMap;
+ // TODO(266379948): Handle group hints too.
ArrayMap<String, Set<AutofillId>> groupHintsToAutofillIdMap =
mClassificationState.mGroupHintsToAutofillIdMap;
@@ -1649,7 +1718,8 @@
for (int i = 0; i < datasets.size(); i++) {
Dataset dataset = datasets.get(i);
- if (dataset.getAutofillDatatypes() == null) continue;
+ if (dataset.getAutofillDatatypes() == null
+ || dataset.getAutofillDatatypes().isEmpty()) continue;
if (dataset.getFieldIds() != null && dataset.getFieldIds().size() > 0) continue;
ArrayList<AutofillId> fieldIds = new ArrayList<>();
@@ -1661,6 +1731,7 @@
ArrayList<Dataset.DatasetFieldFilter> fieldFilters = new ArrayList<>();
for (int j = 0; j < dataset.getAutofillDatatypes().size(); j++) {
+ if (dataset.getAutofillDatatypes().get(0) == null) continue;
String hint = dataset.getAutofillDatatypes().get(j);
if (hintsToAutofillIdMap.containsKey(hint)) {
@@ -4560,7 +4631,7 @@
if (mResponses == null) {
// Set initial capacity as 2 to handle cases where service always requires auth.
// TODO: add a metric for number of responses set by server, so we can use its average
- // as the initial array capacitiy.
+ // as the initial array capacity.
mResponses = new SparseArray<>(2);
}
mResponses.put(requestId, newResponse);
@@ -4982,6 +5053,7 @@
mClassificationGroupHintsMap = new ArrayMap<>();
mHintsToAutofillIdMap = new ArrayMap<>();
mGroupHintsToAutofillIdMap = new ArrayMap<>();
+ mClassificationCombinedHintsMap = new ArrayMap<>();
Set<android.service.assist.classification.FieldClassification> classifications =
response.getClassifications();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 24c9e0f..0b22f36 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -640,6 +640,7 @@
mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class);
setAllowListWhileInUsePermissionInFgs();
initSystemExemptedFgsTypePermission();
+ initMediaProjectFgsTypeCustomPermission();
}
private AppStateTracker getAppStateTracker() {
@@ -2580,6 +2581,39 @@
}
}
+ /**
+ * A custom permission checker for the "mediaProjection" FGS type:
+ * if the app has been granted the permission to start a media projection via
+ * the {@link android.media.project.MediaProjectionManager#createScreenCaptureIntent()},
+ * it'll get the permission to start a foreground service with type "mediaProjection".
+ */
+ private class MediaProjectionFgsTypeCustomPermission extends ForegroundServiceTypePermission {
+ MediaProjectionFgsTypeCustomPermission() {
+ super("Media projection screen capture permission");
+ }
+
+ @Override
+ public int checkPermission(@NonNull Context context, int callerUid, int callerPid,
+ @NonNull String packageName, boolean allowWhileInUse) {
+ return mAm.isAllowedMediaProjectionNoOpCheck(callerUid)
+ ? PERMISSION_GRANTED : PERMISSION_DENIED;
+ }
+ }
+
+ /**
+ * Set a custom permission checker for the "mediaProjection" FGS type.
+ */
+ private void initMediaProjectFgsTypeCustomPermission() {
+ final ForegroundServiceTypePolicy policy = ForegroundServiceTypePolicy.getDefaultPolicy();
+ final ForegroundServiceTypePolicyInfo policyInfo =
+ policy.getForegroundServiceTypePolicyInfo(
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION,
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
+ if (policyInfo != null) {
+ policyInfo.setCustomPermission(new MediaProjectionFgsTypeCustomPermission());
+ }
+ }
+
ServiceNotificationPolicy applyForegroundServiceNotificationLocked(Notification notification,
final String tag, final int id, final String pkg, final int userId) {
// By nature of the FGS API, all FGS notifications have a null tag
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 9e95e5f..5696004 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.app.ActivityThread;
+import android.app.ForegroundServiceTypePolicy;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -1196,6 +1197,7 @@
updateUseTieredCachedAdj();
break;
default:
+ updateFGSPermissionEnforcementFlagsIfNecessary(name);
break;
}
}
@@ -1951,6 +1953,11 @@
DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME);
}
+ private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) {
+ ForegroundServiceTypePolicy.getDefaultPolicy()
+ .updatePermissionEnforcementFlagIfNecessary(name);
+ }
+
@NeverCompile // Avoid size overhead of debugging code.
void dump(PrintWriter pw) {
pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 37f744f..4b2467d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -38,6 +38,8 @@
import static android.app.ActivityManager.StopUserOnSwitch;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
@@ -178,6 +180,7 @@
import android.app.ActivityManagerInternal.BindServiceEventListener;
import android.app.ActivityManagerInternal.BroadcastEventListener;
import android.app.ActivityManagerInternal.ForegroundServiceStateListener;
+import android.app.ActivityManagerInternal.MediaProjectionTokenEvent;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.ActivityThread;
import android.app.AnrController;
@@ -1508,6 +1511,17 @@
final AppRestrictionController mAppRestrictionController;
+ /**
+ * The collection of the MediaProjection tokens per UID, for the apps that are allowed to
+ * start FGS with the type "mediaProjection"; this permission is granted via the request over
+ * the call to {@link android.media.project.MediaProjectionManager#createScreenCaptureIntent()}.
+ *
+ * <p>Note, the "token" here is actually an instance of
+ * {@link android.media.projection.IMediaProjection}.</p>
+ */
+ @GuardedBy("mMediaProjectionTokenMap")
+ private final SparseArray<ArraySet<IBinder>> mMediaProjectionTokenMap = new SparseArray();
+
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
@@ -4929,8 +4943,14 @@
EventLogTags.writeAmProcBound(app.userId, pid, app.processName);
synchronized (mProcLock) {
- mOomAdjuster.setAttachingProcessStatesLSP(app);
+ app.mState.setCurAdj(ProcessList.INVALID_ADJ);
+ app.mState.setSetAdj(ProcessList.INVALID_ADJ);
+ app.mState.setVerifiedAdj(ProcessList.INVALID_ADJ);
+ mOomAdjuster.setAttachingSchedGroupLSP(app);
+ app.mState.setForcingToImportant(null);
clearProcessForegroundLocked(app);
+ app.mState.setHasShownUi(false);
+ app.mState.setCached(false);
app.setDebugging(false);
app.setKilledByAm(false);
app.setKilled(false);
@@ -5098,14 +5118,8 @@
app.makeActive(thread, mProcessStats);
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
}
- app.setPendingFinishAttach(true);
-
updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
-
- updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
- checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
-
final long now = SystemClock.uptimeMillis();
synchronized (mAppProfiler.mProfilerLock) {
app.mProfile.setLastRequestedGc(now);
@@ -5121,6 +5135,8 @@
if (!mConstants.mEnableWaitForFinishAttachApplication) {
finishAttachApplicationInner(startSeq, callingUid, pid);
+ } else {
+ app.setPendingFinishAttach(true);
}
} catch (Exception e) {
// We need kill the process group here. (b/148588589)
@@ -9866,7 +9882,6 @@
boolean dumpNormalPriority = false;
boolean dumpVisibleStacksOnly = false;
boolean dumpFocusedStackOnly = false;
- boolean dumpVerbose = false;
int dumpDisplayId = INVALID_DISPLAY;
String dumpPackage = null;
int dumpUserId = UserHandle.USER_ALL;
@@ -9925,8 +9940,6 @@
return;
}
dumpClient = true;
- } else if ("--verbose".equals(opt)) {
- dumpVerbose = true;
} else if ("-h".equals(opt)) {
ActivityManagerShellCommand.dumpHelp(pw, true);
return;
@@ -10213,8 +10226,7 @@
} else {
// Dumping a single activity?
if (!mAtmInternal.dumpActivity(fd, pw, cmd, args, opti, dumpAll,
- dumpVisibleStacksOnly, dumpFocusedStackOnly, dumpVerbose, dumpDisplayId,
- dumpUserId)) {
+ dumpVisibleStacksOnly, dumpFocusedStackOnly, dumpDisplayId, dumpUserId)) {
ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
int res = shell.exec(this, null, fd, null, args, null,
new ResultReceiver(null));
@@ -17267,6 +17279,9 @@
public ComponentName startSdkSandboxService(Intent service, int clientAppUid,
String clientAppPackage, String processName) throws RemoteException {
validateSdkSandboxParams(service, clientAppUid, clientAppPackage, processName);
+ if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
+ throw new IllegalArgumentException("uid does not belong to provided package");
+ }
// TODO(b/269598719): Is passing the application thread of the system_server alright?
// e.g. the sandbox getting privileged access due to this.
ComponentName cn = ActivityManagerService.this.startService(
@@ -17333,6 +17348,9 @@
String processName, long flags)
throws RemoteException {
validateSdkSandboxParams(service, clientAppUid, clientAppPackage, processName);
+ if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
+ throw new IllegalArgumentException("uid does not belong to provided package");
+ }
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
@@ -17385,9 +17403,6 @@
if (!UserHandle.isApp(clientAppUid)) {
throw new IllegalArgumentException("uid is not within application range");
}
- if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
- throw new IllegalArgumentException("uid does not belong to provided package");
- }
}
@Override
@@ -18141,7 +18156,7 @@
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
final Bundle configChangedOptions = new BroadcastOptions()
.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
- .setDeferUntilActive(true)
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
.toBundle();
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
null, null, OP_NONE, configChangedOptions, false, false, MY_PID, SYSTEM_UID,
@@ -18160,7 +18175,7 @@
PowerExemptionManager.REASON_LOCALE_CHANGED, "");
bOptions.setDeliveryGroupPolicy(
BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
- bOptions.setDeferUntilActive(true);
+ bOptions.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID,
SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(),
@@ -18205,7 +18220,7 @@
final BroadcastOptions options = new BroadcastOptions()
.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
- .setDeferUntilActive(true);
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
if (reason != null) {
options.setDeliveryGroupMatchingKey(Intent.ACTION_CLOSE_SYSTEM_DIALOGS, reason);
}
@@ -18741,6 +18756,12 @@
int uid, int pid) {
ActivityManagerService.this.logFgsApiEnd(apiType, uid, pid);
}
+
+ @Override
+ public void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
+ @MediaProjectionTokenEvent int event) {
+ ActivityManagerService.this.notifyMediaProjectionEvent(uid, projectionToken, event);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
@@ -19974,4 +19995,41 @@
return invalidValue;
}
}
+
+ private void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
+ @MediaProjectionTokenEvent int event) {
+ synchronized (mMediaProjectionTokenMap) {
+ final int index = mMediaProjectionTokenMap.indexOfKey(uid);
+ ArraySet<IBinder> tokens;
+ if (event == MEDIA_PROJECTION_TOKEN_EVENT_CREATED) {
+ if (index < 0) {
+ tokens = new ArraySet();
+ mMediaProjectionTokenMap.put(uid, tokens);
+ } else {
+ tokens = mMediaProjectionTokenMap.valueAt(index);
+ }
+ tokens.add(projectionToken);
+ } else if (event == MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED && index >= 0) {
+ tokens = mMediaProjectionTokenMap.valueAt(index);
+ tokens.remove(projectionToken);
+ if (tokens.isEmpty()) {
+ mMediaProjectionTokenMap.removeAt(index);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return {@code true} if the MediaProjectionManagerService has created a media projection
+ * for the given {@code uid} because the user has granted the permission;
+ * it doesn't necessarily mean it has started the projection.
+ *
+ * <p>It doesn't check the {@link AppOpsManager#OP_PROJECT_MEDIA}.</p>
+ */
+ boolean isAllowedMediaProjectionNoOpCheck(int uid) {
+ synchronized (mMediaProjectionTokenMap) {
+ final int index = mMediaProjectionTokenMap.indexOfKey(uid);
+ return index >= 0 && !mMediaProjectionTokenMap.valueAt(index).isEmpty();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 01bb549..94d08bf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -46,6 +46,7 @@
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
@@ -2222,7 +2223,6 @@
boolean wait = false;
String opt;
int displayId = Display.INVALID_DISPLAY;
- boolean forceInvisible = false;
while ((opt = getNextOption()) != null) {
switch(opt) {
case "-w":
@@ -2231,27 +2231,31 @@
case "--display":
displayId = getDisplayIdFromNextArg();
break;
- case "--force-invisible":
- forceInvisible = true;
- break;
default:
getErrPrintWriter().println("Error: unknown option: " + opt);
return -1;
}
}
final int userId = Integer.parseInt(getNextArgRequired());
- final boolean callStartProfile = !forceInvisible && isProfile(userId);
final ProgressWaiter waiter = wait ? new ProgressWaiter(userId) : null;
- Slogf.d(TAG, "runStartUser(): userId=%d, display=%d, waiter=%s, callStartProfile=%b, "
- + "forceInvisible=%b", userId, displayId, waiter, callStartProfile,
- forceInvisible);
+
+ // For backwards compatibility, if the user is a profile, we need to define whether it
+ // should be started visible (when its parent is the current user) or not (when it isn't)
+ final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+ final ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
+ final int parentUserId = umi.getProfileParentId(userId);
+ final int currentUserId = ami.getCurrentUserId();
+ final boolean isProfile = parentUserId != userId;
+ final boolean isVisibleProfile = isProfile && parentUserId == currentUserId;
+ Slogf.d(TAG, "runStartUser(): userId=%d, parentUserId=%d, currentUserId=%d, isProfile=%b, "
+ + "isVisibleProfile=%b, display=%d, waiter=%s", userId, parentUserId, currentUserId,
+ isProfile, isVisibleProfile, displayId, waiter);
boolean success;
String displaySuffix = "";
-
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shell_runStartUser" + userId);
try {
- if (callStartProfile) {
+ if (isVisibleProfile) {
Slogf.d(TAG, "calling startProfileWithListener(%d, %s)", userId, waiter);
// startProfileWithListener() will start the profile visible (as long its parent is
// the current user), while startUserInBackgroundWithListener() will always start
@@ -3906,11 +3910,6 @@
return new Resources(AssetManager.getSystem(), metrics, config);
}
- private boolean isProfile(@UserIdInt int userId) {
- final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
- return umi.getProfileParentId(userId) != userId;
- }
-
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -3957,7 +3956,6 @@
pw.println(" --checkin: output checkin format, resetting data.");
pw.println(" --C: output checkin format, not resetting data.");
pw.println(" --proto: output dump in protocol buffer format.");
- pw.println(" --verbose: dumps extra information.");
pw.printf(" %s: dump just the DUMPABLE-related state of an activity. Use the %s "
+ "option to list the supported DUMPABLEs\n", Activity.DUMP_ARG_DUMP_DUMPABLE,
Activity.DUMP_ARG_LIST_DUMPABLES);
@@ -4158,7 +4156,7 @@
pw.println(" execution of that user if it is currently stopped.");
pw.println(" get-current-user");
pw.println(" Returns id of the current foreground user.");
- pw.println(" start-user [-w] [--display DISPLAY_ID] [--force-invisible] <USER_ID>");
+ pw.println(" start-user [-w] [--display DISPLAY_ID] <USER_ID>");
pw.println(" Start USER_ID in background if it is currently stopped;");
pw.println(" use switch-user if you want to start the user in foreground.");
pw.println(" -w: wait for start-user to complete and the user to be unlocked.");
@@ -4166,10 +4164,6 @@
+ "which allows the user to launch activities on it.");
pw.println(" (not supported on all devices; typically only on automotive builds "
+ "where the vehicle has passenger displays)");
- pw.println(" --force-invisible: always start the user invisible, even if it's a "
- + "profile.");
- pw.println(" (by default, a profile is visible in the default display when its "
- + "parent is the current foreground user)");
pw.println(" unlock-user <USER_ID>");
pw.println(" Unlock the given user. This will only work if the user doesn't");
pw.println(" have an LSKF (PIN/pattern/password).");
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 81ca267..bef16b6 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -52,7 +52,6 @@
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -224,22 +223,12 @@
private static final int MSG_DELIVERY_TIMEOUT_HARD = 3;
private static final int MSG_BG_ACTIVITY_START_TIMEOUT = 4;
private static final int MSG_CHECK_HEALTH = 5;
- private static final int MSG_FINISH_RECEIVER = 6;
private void enqueueUpdateRunningList() {
mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
mLocalHandler.sendEmptyMessage(MSG_UPDATE_RUNNING_LIST);
}
- private void enqueueFinishReceiver(@NonNull BroadcastProcessQueue queue,
- @DeliveryState int deliveryState, @NonNull String reason) {
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = queue;
- args.argi1 = deliveryState;
- args.arg2 = reason;
- mLocalHandler.sendMessage(Message.obtain(mLocalHandler, MSG_FINISH_RECEIVER, args));
- }
-
private final Handler mLocalHandler;
private final Handler.Callback mLocalCallback = (msg) -> {
@@ -278,17 +267,6 @@
}
return true;
}
- case MSG_FINISH_RECEIVER: {
- synchronized (mService) {
- final SomeArgs args = (SomeArgs) msg.obj;
- final BroadcastProcessQueue queue = (BroadcastProcessQueue) args.arg1;
- final int deliveryState = args.argi1;
- final String reason = (String) args.arg2;
- args.recycle();
- finishReceiverActiveLocked(queue, deliveryState, reason);
- }
- return true;
- }
}
return false;
};
@@ -727,7 +705,7 @@
// Ignore registered receivers from a previous PID
if (receiver instanceof BroadcastFilter) {
mRunningColdStart = null;
- enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_SKIPPED,
+ finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED,
"BroadcastFilter for cold app");
return;
}
@@ -758,7 +736,7 @@
hostingRecord, zygotePolicyFlags, allowWhileBooting, false);
if (queue.app == null) {
mRunningColdStart = null;
- enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE,
+ finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
"startProcessLocked failed");
return;
}
@@ -800,7 +778,7 @@
@NonNull BroadcastRecord r, int index) {
final String reason = shouldSkipReceiver(queue, r, index);
if (reason != null) {
- enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_SKIPPED, reason);
+ finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED, reason);
return true;
}
return false;
@@ -914,7 +892,7 @@
// TODO: consider making registered receivers of unordered
// broadcasts report results to detect ANRs
if (assumeDelivered) {
- enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_DELIVERED,
+ finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_DELIVERED,
"assuming delivered");
}
} else {
@@ -932,10 +910,10 @@
logw(msg);
app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
- enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE, "remote app");
+ finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE, "remote app");
}
} else {
- enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE,
+ finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
"missing IApplicationThread");
}
}
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 3792625..9ff2cd0 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -31,11 +31,17 @@
// process/eventType) further entries will be rejected until RATE_LIMIT_BUFFER_DURATION has
// elapsed, after which the current count for this breakdown will be reset.
private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.MINUTE_IN_MILLIS;
- // The time duration after which the rate limit buffer will be cleared.
- private static final long RATE_LIMIT_BUFFER_EXPIRY = 3 * RATE_LIMIT_BUFFER_DURATION;
+ // Indicated how many buffer durations to wait before the rate limit buffer will be cleared.
+ // E.g. if set to 3 will wait 3xRATE_LIMIT_BUFFER_DURATION before clearing the buffer.
+ private static final long RATE_LIMIT_BUFFER_EXPIRY_FACTOR = 3;
// The number of entries to keep per breakdown of process/eventType.
private static final int RATE_LIMIT_ALLOWED_ENTRIES = 6;
+ // If a process is rate limited twice in a row we consider it crash-looping and rate limit it
+ // more aggressively.
+ private static final int STRICT_RATE_LIMIT_ALLOWED_ENTRIES = 1;
+ private static final long STRICT_RATE_LIMIT_BUFFER_DURATION = 60 * DateUtils.MINUTE_IN_MILLIS;
+
@GuardedBy("mErrorClusterRecords")
private final ArrayMap<String, ErrorRecord> mErrorClusterRecords = new ArrayMap<>();
private final Clock mClock;
@@ -71,15 +77,27 @@
return new RateLimitResult(false, 0);
}
- if (now - errRecord.getStartTime() > RATE_LIMIT_BUFFER_DURATION) {
+ final long timeSinceFirstError = now - errRecord.getStartTime();
+ if (timeSinceFirstError > errRecord.getBufferDuration()) {
final int errCount = recentlyDroppedCount(errRecord);
errRecord.setStartTime(now);
errRecord.setCount(1);
+
+ // If this error happened exactly the next "rate limiting cycle" after the last
+ // error and the previous cycle was rate limiting then increment the successive
+ // rate limiting cycle counter. If a full "cycle" has passed since the last error
+ // then this is no longer a continuous occurrence and will be rate limited normally.
+ if (errCount > 0 && timeSinceFirstError < 2 * errRecord.getBufferDuration()) {
+ errRecord.incrementSuccessiveRateLimitCycles();
+ } else {
+ errRecord.setSuccessiveRateLimitCycles(0);
+ }
+
return new RateLimitResult(false, errCount);
}
errRecord.incrementCount();
- if (errRecord.getCount() > RATE_LIMIT_ALLOWED_ENTRIES) {
+ if (errRecord.getCount() > errRecord.getAllowedEntries()) {
return new RateLimitResult(true, recentlyDroppedCount(errRecord));
}
}
@@ -91,16 +109,19 @@
* dropped. Resets every RATE_LIMIT_BUFFER_DURATION if events are still actively created or
* RATE_LIMIT_BUFFER_EXPIRY if not. */
private int recentlyDroppedCount(ErrorRecord errRecord) {
- if (errRecord == null || errRecord.getCount() < RATE_LIMIT_ALLOWED_ENTRIES) return 0;
- return errRecord.getCount() - RATE_LIMIT_ALLOWED_ENTRIES;
+ if (errRecord == null || errRecord.getCount() < errRecord.getAllowedEntries()) return 0;
+ return errRecord.getCount() - errRecord.getAllowedEntries();
}
- private void maybeRemoveExpiredRecords(long now) {
- if (now - mLastMapCleanUp <= RATE_LIMIT_BUFFER_EXPIRY) return;
+ private void maybeRemoveExpiredRecords(long currentTime) {
+ if (currentTime - mLastMapCleanUp
+ <= RATE_LIMIT_BUFFER_EXPIRY_FACTOR * RATE_LIMIT_BUFFER_DURATION) {
+ return;
+ }
for (int i = mErrorClusterRecords.size() - 1; i >= 0; i--) {
- if (now - mErrorClusterRecords.valueAt(i).getStartTime() > RATE_LIMIT_BUFFER_EXPIRY) {
+ if (mErrorClusterRecords.valueAt(i).hasExpired(currentTime)) {
Counter.logIncrement(
"stability_errors.value_dropbox_buffer_expired_count",
mErrorClusterRecords.valueAt(i).getCount());
@@ -108,7 +129,7 @@
}
}
- mLastMapCleanUp = now;
+ mLastMapCleanUp = currentTime;
}
/** Resets the rate limiter memory. */
@@ -153,10 +174,12 @@
private class ErrorRecord {
long mStartTime;
int mCount;
+ int mSuccessiveRateLimitCycles;
ErrorRecord(long startTime, int count) {
mStartTime = startTime;
mCount = count;
+ mSuccessiveRateLimitCycles = 0;
}
public void setStartTime(long startTime) {
@@ -171,6 +194,14 @@
mCount++;
}
+ public void setSuccessiveRateLimitCycles(int successiveRateLimitCycles) {
+ mSuccessiveRateLimitCycles = successiveRateLimitCycles;
+ }
+
+ public void incrementSuccessiveRateLimitCycles() {
+ mSuccessiveRateLimitCycles++;
+ }
+
public long getStartTime() {
return mStartTime;
}
@@ -178,6 +209,27 @@
public int getCount() {
return mCount;
}
+
+ public int getSuccessiveRateLimitCycles() {
+ return mSuccessiveRateLimitCycles;
+ }
+
+ public boolean isRepeated() {
+ return mSuccessiveRateLimitCycles >= 2;
+ }
+
+ public int getAllowedEntries() {
+ return isRepeated() ? STRICT_RATE_LIMIT_ALLOWED_ENTRIES : RATE_LIMIT_ALLOWED_ENTRIES;
+ }
+
+ public long getBufferDuration() {
+ return isRepeated() ? STRICT_RATE_LIMIT_BUFFER_DURATION : RATE_LIMIT_BUFFER_DURATION;
+ }
+
+ public boolean hasExpired(long currentTime) {
+ long bufferExpiry = RATE_LIMIT_BUFFER_EXPIRY_FACTOR * getBufferDuration();
+ return currentTime - mStartTime > bufferExpiry;
+ }
}
private static class DefaultClock implements Clock {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 7121421..a944f6f 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1260,19 +1260,12 @@
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
- if (!app.isKilledByAm() && app.getThread() != null) {
+ if (!app.isKilledByAm() && app.getThread() != null && !app.isPendingFinishAttach()) {
// We don't need to apply the update for the process which didn't get computed
if (state.getCompletedAdjSeq() == mAdjSeq) {
applyOomAdjLSP(app, true, now, nowElapsed, oomAdjReason);
}
- if (app.isPendingFinishAttach()) {
- // Avoid trimming processes that are still initializing. If they aren't
- // hosting any components yet because they may be unfairly killed.
- // We however apply any computed previously computed oom scores before skipping.
- continue;
- }
-
final ProcessServiceRecord psr = app.mServices;
// Count the number of process types.
switch (state.getCurProcState()) {
@@ -1706,19 +1699,6 @@
return false;
}
- if (app.isPendingFinishAttach()) {
- state.setAdjSeq(mAdjSeq);
- state.setCompletedAdjSeq(mAdjSeq);
- // If the process is still initializing, we skip computing any states because we
- // don't want to override the special states that have been set at
- // AMS#attachApplication with OomAdjuster#setAttachingProcessStates.
- // In this limbo state, the app has |PROC_START_TIMEOUT| to finish attach application
- // and receive updated proc_state based on its importance.
- // Note that in this state, the oom_score is INVALID_ADJ which is outside the standard
- // oom score range and the app is safe from lmkd kills.
- return false;
- }
-
state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN);
state.setAdjSource(null);
state.setAdjTarget(null);
@@ -3239,7 +3219,7 @@
}
@GuardedBy({"mService", "mProcLock"})
- void setAttachingProcessStatesLSP(ProcessRecord app) {
+ void setAttachingSchedGroupLSP(ProcessRecord app) {
int initialSchedGroup = SCHED_GROUP_DEFAULT;
final ProcessStateRecord state = app.mState;
// If the process has been marked as foreground, it is starting as the top app (with
@@ -3259,15 +3239,6 @@
state.setSetSchedGroup(initialSchedGroup);
state.setCurrentSchedulingGroup(initialSchedGroup);
- state.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
- state.setCurCapability(PROCESS_CAPABILITY_NONE);
-
- state.setCurAdj(ProcessList.FOREGROUND_APP_ADJ);
- state.setSetAdj(ProcessList.FOREGROUND_APP_ADJ);
- state.setVerifiedAdj(ProcessList.FOREGROUND_APP_ADJ);
- state.setForcingToImportant(null);
- state.setHasShownUi(false);
- state.setCached(true);
}
// ONLY used for unit testing in OomAdjusterTests.java
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
index dd06464..a028ae1 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
@@ -32,7 +32,7 @@
* Trace.traceEnd. These traces are used for performance testing.
*/
public class AppOpsCheckingServiceTracingDecorator implements AppOpsCheckingServiceInterface {
- private static final long TRACE_TAG = Trace.TRACE_TAG_SYSTEM_SERVER;
+ private static final long TRACE_TAG = Trace.TRACE_TAG_ACTIVITY_MANAGER;
private final AppOpsCheckingServiceInterface mService;
AppOpsCheckingServiceTracingDecorator(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c50e275..490a33e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3928,8 +3928,10 @@
}
@Override
- @android.annotation.EnforcePermission(anyOf =
- {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
/** @see AudioDeviceVolumeManager#setDeviceVolume(VolumeInfo, AudioDeviceAttributes)
* Part of service interface, check permissions and parameters here
* Note calling package is for logging purposes only, not to be trusted
@@ -4945,8 +4947,10 @@
}
@Override
- @android.annotation.EnforcePermission(anyOf =
- {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
/**
* @see AudioDeviceVolumeManager#getDeviceVolume(VolumeInfo, AudioDeviceAttributes)
*/
@@ -7214,8 +7218,10 @@
* @param device the audio device to be affected
* @param deviceVolumeBehavior one of the device behaviors
*/
- @android.annotation.EnforcePermission(anyOf =
- {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
// verify permissions
@@ -7295,8 +7301,11 @@
* @param device the audio output device type
* @return the volume behavior for the device
*/
- @android.annotation.EnforcePermission(anyOf =
- {"MODIFY_AUDIO_ROUTING", "QUERY_AUDIO_STATE", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
public @AudioManager.DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
// verify permissions
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 12134f7..4e3de3c 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -499,7 +499,8 @@
if ((recentTasks != null) && (!recentTasks.getList().isEmpty())) {
for (ActivityManager.RecentTaskInfo task : recentTasks.getList()) {
- if (packageName.equals(task.topActivityInfo.packageName)) {
+ if (task.topActivityInfo != null && packageName.equals(
+ task.topActivityInfo.packageName)) {
taskInfo = new TaskInfo();
taskInfo.frontTaskId = task.taskId;
taskInfo.isResizeable =
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1ce917c..6d3f8fd 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -21,6 +21,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
@@ -29,6 +30,7 @@
import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_AUTO;
import static android.os.PowerWhitelistManager.REASON_VPN;
import static android.os.UserHandle.PER_USER_RANGE;
+import static android.telephony.CarrierConfigManager.KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
@@ -79,7 +81,9 @@
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.NetworkScore;
+import android.net.NetworkSpecifier;
import android.net.RouteInfo;
+import android.net.TelephonyNetworkSpecifier;
import android.net.UidRangeParcel;
import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
@@ -127,12 +131,16 @@
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyPermission;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Log;
import android.util.Range;
+import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -268,6 +276,10 @@
private final ConnectivityManager mConnectivityManager;
private final AppOpsManager mAppOpsManager;
private final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager;
+ private final TelephonyManager mTelephonyManager;
+ private final CarrierConfigManager mCarrierConfigManager;
+ private final SubscriptionManager mSubscriptionManager;
+
// The context is for specific user which is created from mUserId
private final Context mUserIdContext;
@VisibleForTesting final Dependencies mDeps;
@@ -314,6 +326,14 @@
private final LocalLog mVpnManagerEvents = new LocalLog(MAX_EVENTS_LOGS);
/**
+ * Cached Map of <subscription ID, keepalive delay ms> since retrieving the PersistableBundle
+ * and the target value from CarrierConfigManager is somewhat expensive as it has hundreds of
+ * fields. This cache is cleared when the carrier config changes to ensure data freshness.
+ */
+ @GuardedBy("this")
+ private final SparseArray<Integer> mCachedKeepalivePerSubId = new SparseArray<>();
+
+ /**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
* only applies to {@link VpnService} connections.
*/
@@ -626,6 +646,10 @@
mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
mConnectivityDiagnosticsManager =
mContext.getSystemService(ConnectivityDiagnosticsManager.class);
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+
mDeps = deps;
mNms = netService;
mNetd = netd;
@@ -2893,6 +2917,24 @@
*/
private int mRetryCount = 0;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+ new CarrierConfigManager.CarrierConfigChangeListener() {
+ @Override
+ public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+ int specificCarrierId) {
+ synchronized (Vpn.this) {
+ mCachedKeepalivePerSubId.remove(subId);
+
+ // Ignore stale runner.
+ if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
+
+ maybeMigrateIkeSession(mActiveNetwork);
+ }
+ // TODO: update the longLivedTcpConnectionsExpensive value in the
+ // networkcapabilities of the VPN network.
+ }
+ };
+
IkeV2VpnRunner(
@NonNull Ikev2VpnProfile profile, @NonNull ScheduledThreadPoolExecutor executor) {
super(TAG);
@@ -2918,6 +2960,9 @@
setVpnNetworkPreference(mSessionKey,
createUserAndRestrictedProfilesRanges(mUserId,
mConfig.allowedApplications, mConfig.disallowedApplications));
+
+ mCarrierConfigManager.registerCarrierConfigChangeListener(mExecutor,
+ mCarrierConfigChangeListener);
}
@Override
@@ -3257,8 +3302,6 @@
mUnderlyingLinkProperties = null;
mUnderlyingNetworkCapabilities = null;
mRetryCount = 0;
-
- startOrMigrateIkeSession(network);
}
@NonNull
@@ -3356,9 +3399,36 @@
}
private int guessNattKeepaliveTimerForNetwork() {
- // TODO : guess the keepalive delay based on carrier if auto keepalive timer is
- // enabled
- return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ final int subId = getCellSubIdForNetworkCapabilities(mUnderlyingNetworkCapabilities);
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ Log.d(TAG, "Underlying network is not a cellular network");
+ return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ }
+
+ synchronized (Vpn.this) {
+ if (mCachedKeepalivePerSubId.contains(subId)) {
+ Log.d(TAG, "Get cached keepalive config");
+ return mCachedKeepalivePerSubId.get(subId);
+ }
+
+ final TelephonyManager perSubTm = mTelephonyManager.createForSubscriptionId(subId);
+ if (perSubTm.getSimApplicationState() != TelephonyManager.SIM_STATE_LOADED) {
+ Log.d(TAG, "SIM card is not ready on sub " + subId);
+ return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ }
+
+ final PersistableBundle carrierConfig =
+ mCarrierConfigManager.getConfigForSubId(subId);
+ if (!CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
+ return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ }
+
+ final int natKeepalive =
+ carrierConfig.getInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT);
+ mCachedKeepalivePerSubId.put(subId, natKeepalive);
+ Log.d(TAG, "Get customized keepalive=" + natKeepalive);
+ return natKeepalive;
+ }
}
boolean maybeMigrateIkeSession(@NonNull Network underlyingNetwork) {
@@ -3456,7 +3526,15 @@
/** Called when the NetworkCapabilities of underlying network is changed */
public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
+ final NetworkCapabilities oldNc = mUnderlyingNetworkCapabilities;
mUnderlyingNetworkCapabilities = nc;
+ if (oldNc == null) {
+ // A new default network is available.
+ startOrMigrateIkeSession(mActiveNetwork);
+ } else if (!nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) {
+ // Renew carrierConfig values.
+ maybeMigrateIkeSession(mActiveNetwork);
+ }
}
/** Called when the LinkProperties of underlying network is changed */
@@ -3812,6 +3890,8 @@
resetIkeState();
+ mCarrierConfigManager.unregisterCarrierConfigChangeListener(
+ mCarrierConfigChangeListener);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
mDiagnosticsCallback);
@@ -4804,6 +4884,8 @@
pw.println("Reset session scheduled");
}
}
+ pw.println("mCachedKeepalivePerSubId=" + mCachedKeepalivePerSubId);
+
pw.println("mUnderlyNetworkChanges (most recent first):");
pw.increaseIndent();
mUnderlyNetworkChanges.reverseDump(pw);
@@ -4815,4 +4897,19 @@
pw.decreaseIndent();
}
}
+
+ private static int getCellSubIdForNetworkCapabilities(@Nullable NetworkCapabilities nc) {
+ if (nc == null) return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ if (!nc.hasTransport(TRANSPORT_CELLULAR)) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ final NetworkSpecifier specifier = nc.getNetworkSpecifier();
+ if (specifier instanceof TelephonyNetworkSpecifier) {
+ return ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+ }
+
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
}
diff --git a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
index 1bb34ab..f93d9ee 100644
--- a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
+++ b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
@@ -84,7 +84,11 @@
@Override
public void notifyThrottling(Temperature temp) {
@Temperature.ThrottlingStatus int currentStatus = temp.getStatus();
+
synchronized (mThermalObserverLock) {
+ if (mStatus == currentStatus) {
+ return; // status not changed, skip update
+ }
mStatus = currentStatus;
mHandler.post(this::updateVotes);
}
@@ -188,6 +192,10 @@
localStatus = mStatus;
localMap = mThermalThrottlingByDisplay.get(displayId);
}
+ if (localMap == null) {
+ Slog.d(TAG, "Updating votes, display already removed, display=" + displayId);
+ return;
+ }
if (mLoggingEnabled) {
Slog.d(TAG, "Updating votes for status=" + localStatus + ", display =" + displayId
+ ", map=" + localMap);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java b/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java
index dc2799e..6cd2493 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java
@@ -21,6 +21,8 @@
import android.app.ActivityThread;
import android.provider.DeviceConfig;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Class for the device-level configuration related to the input method manager
* platform features in {@link DeviceConfig}.
@@ -56,4 +58,9 @@
public boolean shouldHideImeWhenNoEditorFocus() {
return mHideImeWhenNoEditorFocus;
}
+
+ @VisibleForTesting
+ void destroy() {
+ DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index cc41207..afae08d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -56,7 +56,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DeviceStateCache;
@@ -2514,7 +2514,7 @@
* Starts a session to verify lock screen credentials provided by a remote device.
*/
@NonNull
- public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+ public RemoteLockscreenValidationSession startRemoteLockscreenValidation() {
return mRecoverableKeyStoreManager.startRemoteLockscreenValidation(this);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index c08958b..f073756 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -31,7 +31,7 @@
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
import android.content.Context;
import android.os.Binder;
import android.os.RemoteException;
@@ -999,7 +999,7 @@
/**
* Starts a session to verify lock screen credentials provided by a remote device.
*/
- public StartLockscreenValidationRequest startRemoteLockscreenValidation(
+ public RemoteLockscreenValidationSession startRemoteLockscreenValidation(
LockSettingsService lockSettingsService) {
if (mRemoteLockscreenValidationSessionStorage == null) {
throw new UnsupportedOperationException("Under development");
@@ -1021,8 +1021,8 @@
int badGuesses = mDatabase.getBadRemoteGuessCounter(userId);
int remainingAttempts = Math.max(INVALID_REMOTE_GUESS_LIMIT - badGuesses, 0);
// TODO(b/254335492): Schedule task to remove inactive session
- return new StartLockscreenValidationRequest.Builder()
- .setLockscreenUiType(keyguardCredentialsType)
+ return new RemoteLockscreenValidationSession.Builder()
+ .setLockType(keyguardCredentialsType)
.setRemainingAttempts(remainingAttempts)
.setSourcePublicKey(encodedPublicKey)
.build();
@@ -1046,7 +1046,9 @@
.build();
}
if (session == null) {
- throw new IllegalStateException("There is no active lock screen check session");
+ return new RemoteLockscreenValidationResult.Builder()
+ .setResultCode(RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED)
+ .build();
}
byte[] decryptedCredentials;
try {
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 77b9abe..4832618 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -16,6 +16,9 @@
package com.android.server.media.projection;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -484,6 +487,9 @@
userHandle = new UserHandle(UserHandle.getUserId(uid));
mTargetSdkVersion = targetSdkVersion;
mIsPrivileged = isPrivileged;
+ // TODO(b/267740338): Add unit test.
+ mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
+ MEDIA_PROJECTION_TOKEN_EVENT_CREATED);
}
@Override // Binder call
@@ -630,6 +636,9 @@
mToken = null;
unregisterCallback(mCallback);
mCallback = null;
+ // TODO(b/267740338): Add unit test.
+ mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
+ MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED);
}
}
diff --git a/services/core/java/com/android/server/notification/OWNERS b/services/core/java/com/android/server/notification/OWNERS
index 5a19656..6c4dd6d 100644
--- a/services/core/java/com/android/server/notification/OWNERS
+++ b/services/core/java/com/android/server/notification/OWNERS
@@ -1,4 +1,6 @@
-dsandler@android.com
+# Bug component: 34005
+
juliacr@google.com
-beverlyt@google.com
-pixel@google.com
+yurilin@google.com
+jeffdq@google.com
+dsandler@android.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 20cb485..50f88d3 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1645,7 +1645,7 @@
return false;
}
int uid = injectGetPackageUid(systemChooser.getPackageName(), UserHandle.USER_SYSTEM);
- return uid == callingUid;
+ return UserHandle.getAppId(uid) == UserHandle.getAppId(callingUid);
}
private void enforceSystemOrShell() {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2f98d34..3343172 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3504,15 +3504,15 @@
return;
}
final int oldMainUserId = getMainUserIdUnchecked();
- final int oldFlags = systemUserData.info.flags;
- final int newFlags;
+ final int oldSysFlags = systemUserData.info.flags;
+ final int newSysFlags;
final String newUserType;
if (newHeadlessSystemUserMode) {
newUserType = UserManager.USER_TYPE_SYSTEM_HEADLESS;
- newFlags = oldFlags & ~UserInfo.FLAG_FULL & ~UserInfo.FLAG_MAIN;
+ newSysFlags = oldSysFlags & ~UserInfo.FLAG_FULL & ~UserInfo.FLAG_MAIN;
} else {
newUserType = UserManager.USER_TYPE_FULL_SYSTEM;
- newFlags = oldFlags | UserInfo.FLAG_FULL;
+ newSysFlags = oldSysFlags | UserInfo.FLAG_FULL | UserInfo.FLAG_MAIN;
}
if (systemUserData.info.userType.equals(newUserType)) {
@@ -3523,18 +3523,19 @@
Slogf.i(LOG_TAG, "Persisting emulated system user data: type changed from %s to "
+ "%s, flags changed from %s to %s",
systemUserData.info.userType, newUserType,
- UserInfo.flagsToString(oldFlags), UserInfo.flagsToString(newFlags));
+ UserInfo.flagsToString(oldSysFlags), UserInfo.flagsToString(newSysFlags));
systemUserData.info.userType = newUserType;
- systemUserData.info.flags = newFlags;
+ systemUserData.info.flags = newSysFlags;
writeUserLP(systemUserData);
- // Switch the MainUser to a reasonable choice if needed.
- // (But if there was no MainUser, we deliberately continue to have no MainUser.)
+ // Designate the MainUser to a reasonable choice if needed.
final UserData oldMain = getUserDataNoChecks(oldMainUserId);
if (newHeadlessSystemUserMode) {
- if (oldMain != null && (oldMain.info.flags & UserInfo.FLAG_SYSTEM) != 0) {
- // System was MainUser. So we need a new choice for Main. Pick the oldest.
+ final boolean mainIsAlreadyNonSystem =
+ oldMain != null && (oldMain.info.flags & UserInfo.FLAG_SYSTEM) == 0;
+ if (!mainIsAlreadyNonSystem && isMainUserPermanentAdmin()) {
+ // We need a new choice for Main. Pick the oldest.
// If no oldest, don't set any. Let the BootUserInitializer do that later.
final UserInfo newMainUser = getEarliestCreatedFullUser();
if (newMainUser != null) {
@@ -3544,16 +3545,16 @@
}
}
} else {
+ // We already made user 0 Main above. Now strip it from the old Main user.
// TODO(b/256624031): For now, we demand the Main user (if there is one) is
// always the system in non-HSUM. In the future, when we relax this, change how
// we handle MAIN.
if (oldMain != null && (oldMain.info.flags & UserInfo.FLAG_SYSTEM) == 0) {
- // Someone else was the MainUser; transfer it to System.
Slogf.i(LOG_TAG, "Transferring Main to user 0 from " + oldMain.info.id);
oldMain.info.flags &= ~UserInfo.FLAG_MAIN;
- systemUserData.info.flags |= UserInfo.FLAG_MAIN;
writeUserLP(oldMain);
- writeUserLP(systemUserData);
+ } else {
+ Slogf.i(LOG_TAG, "Designated user 0 to be Main");
}
}
}
@@ -3817,12 +3818,14 @@
if (userVersion < 11) {
// Add FLAG_MAIN
if (isHeadlessSystemUserMode()) {
- final UserInfo earliestCreatedUser = getEarliestCreatedFullUser();
- if (earliestCreatedUser != null) {
- earliestCreatedUser.flags |= UserInfo.FLAG_MAIN;
- userIdsToWrite.add(earliestCreatedUser.id);
+ if (isMainUserPermanentAdmin()) {
+ final UserInfo earliestCreatedUser = getEarliestCreatedFullUser();
+ if (earliestCreatedUser != null) {
+ earliestCreatedUser.flags |= UserInfo.FLAG_MAIN;
+ userIdsToWrite.add(earliestCreatedUser.id);
+ }
}
- } else {
+ } else { // not isHeadlessSystemUserMode
synchronized (mUsersLock) {
final UserData userData = mUsers.get(UserHandle.USER_SYSTEM);
userData.info.flags |= UserInfo.FLAG_MAIN;
diff --git a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
index 98b24ea..333c98c 100644
--- a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
+++ b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
@@ -380,6 +380,7 @@
final int pid = Process.myPid();
Slogf.i(LOG_TAG, "Restarting Android runtime(PID=%d) to finalize changes", pid);
pw.println("Restarting Android runtime to finalize changes");
+ pw.println("The restart may trigger a 'Broken pipe' message; this is to be expected.");
pw.flush();
// Ideally there should be a cleaner / safer option to restart system_server, but
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
index f87f50a..a8615c2 100644
--- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -108,7 +108,7 @@
private final Object mLock = new Object();
private final boolean mVisibleBackgroundUsersEnabled;
- private final boolean mVisibleBackgroundUserOnDefaultDisplayAllowed;
+ private final boolean mVisibleBackgroundUserOnDefaultDisplayEnabled;
@UserIdInt
@GuardedBy("mLock")
@@ -168,11 +168,17 @@
}
@VisibleForTesting
- UserVisibilityMediator(boolean backgroundUsersOnDisplaysEnabled,
- boolean visibleBackgroundUserOnDefaultDisplayAllowed, Handler handler) {
- mVisibleBackgroundUsersEnabled = backgroundUsersOnDisplaysEnabled;
- mVisibleBackgroundUserOnDefaultDisplayAllowed =
- visibleBackgroundUserOnDefaultDisplayAllowed;
+ UserVisibilityMediator(boolean visibleBackgroundUsersOnDisplaysEnabled,
+ boolean visibleBackgroundUserOnDefaultDisplayEnabled, Handler handler) {
+ mVisibleBackgroundUsersEnabled = visibleBackgroundUsersOnDisplaysEnabled;
+ if (visibleBackgroundUserOnDefaultDisplayEnabled
+ && !visibleBackgroundUsersOnDisplaysEnabled) {
+ throw new IllegalArgumentException("Cannot have "
+ + "visibleBackgroundUserOnDefaultDisplayEnabled without "
+ + "visibleBackgroundUsersOnDisplaysEnabled");
+ }
+ mVisibleBackgroundUserOnDefaultDisplayEnabled =
+ visibleBackgroundUserOnDefaultDisplayEnabled;
if (mVisibleBackgroundUsersEnabled) {
mUsersAssignedToDisplayOnStart = new SparseIntArray();
mExtraDisplaysAssignedToUsers = new SparseIntArray();
@@ -318,14 +324,14 @@
boolean visibleBackground = userStartMode == USER_START_MODE_BACKGROUND_VISIBLE;
if (displayId == DEFAULT_DISPLAY && visibleBackground) {
- if (mVisibleBackgroundUserOnDefaultDisplayAllowed && isCurrentUserLocked(userId)) {
+ if (mVisibleBackgroundUserOnDefaultDisplayEnabled && isCurrentUserLocked(userId)) {
// Shouldn't happen - UserController returns before calling this method
Slogf.wtf(TAG, "trying to start current user (%d) visible in background on default"
+ " display", userId);
return USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE;
}
- if (!mVisibleBackgroundUserOnDefaultDisplayAllowed
+ if (!mVisibleBackgroundUserOnDefaultDisplayEnabled
&& !isProfile(userId, profileGroupId)) {
Slogf.wtf(TAG, "cannot start full user (%d) visible on default display", userId);
return USER_ASSIGNMENT_RESULT_FAILURE;
@@ -383,7 +389,7 @@
}
return foreground || displayId != DEFAULT_DISPLAY
- || (visibleBackground && mVisibleBackgroundUserOnDefaultDisplayAllowed)
+ || (visibleBackground && mVisibleBackgroundUserOnDefaultDisplayEnabled)
? USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE
: USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
}
@@ -394,7 +400,7 @@
@UserIdInt int profileGroupId, @UserStartMode int userStartMode, int displayId) {
if (displayId == DEFAULT_DISPLAY) {
boolean mappingNeeded = false;
- if (mVisibleBackgroundUserOnDefaultDisplayAllowed
+ if (mVisibleBackgroundUserOnDefaultDisplayEnabled
&& userStartMode == USER_START_MODE_BACKGROUND_VISIBLE) {
int userStartedOnDefaultDisplay = getUserStartedOnDisplay(DEFAULT_DISPLAY);
if (userStartedOnDefaultDisplay != USER_NULL
@@ -752,7 +758,7 @@
*/
public int getDisplayAssignedToUser(@UserIdInt int userId) {
if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
- if (mVisibleBackgroundUserOnDefaultDisplayAllowed) {
+ if (mVisibleBackgroundUserOnDefaultDisplayEnabled) {
// When device supports visible bg users on default display, the default display is
// assigned to the current user, unless a user is started visible on it
int userStartedOnDefaultDisplay;
@@ -801,7 +807,7 @@
private @UserIdInt int getUserAssignedToDisplay(@UserIdInt int displayId,
boolean returnCurrentUserByDefault) {
if (returnCurrentUserByDefault
- && ((displayId == DEFAULT_DISPLAY && !mVisibleBackgroundUserOnDefaultDisplayAllowed
+ && ((displayId == DEFAULT_DISPLAY && !mVisibleBackgroundUserOnDefaultDisplayEnabled
|| !mVisibleBackgroundUsersEnabled))) {
return getCurrentUserId();
}
@@ -961,8 +967,8 @@
ipw.print("Supports visible background users on displays: ");
ipw.println(mVisibleBackgroundUsersEnabled);
- ipw.print("Allows visible background users on default display: ");
- ipw.println(mVisibleBackgroundUserOnDefaultDisplayAllowed);
+ ipw.print("Supports visible background users on default display: ");
+ ipw.println(mVisibleBackgroundUserOnDefaultDisplayEnabled);
dumpSparseIntArray(ipw, mUsersAssignedToDisplayOnStart, "user / display", "u", "d");
dumpSparseIntArray(ipw, mExtraDisplaysAssignedToUsers, "extra display / user",
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 85f1357..646dc4e 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@
import com.android.internal.policy.IKeyguardService;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.EventLogTags;
import java.io.PrintWriter;
@@ -255,6 +256,11 @@
public void setOccluded(boolean isOccluded, boolean notify) {
if (mKeyguardService != null && notify) {
if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ")");
+ EventLogTags.writeWmSetKeyguardOccluded(
+ isOccluded ? 1 : 0,
+ 0 /* animate */,
+ 0 /* transit */,
+ "setOccluded");
mKeyguardService.setOccluded(isOccluded, false /* animate */);
}
mKeyguardState.occluded = isOccluded;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index c6684df..5442b6d 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2125,7 +2125,7 @@
}
@Override
- public void notifyTvMessage(IBinder sessionToken, String type, Bundle data, int userId) {
+ public void notifyTvMessage(IBinder sessionToken, int type, Bundle data, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "timeShiftEnablePositionTracking");
@@ -2590,7 +2590,7 @@
}
@Override
- public void notifyAdBuffer(
+ public void notifyAdBufferReady(
IBinder sessionToken, AdBuffer buffer, int userId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -2602,7 +2602,7 @@
try {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
- getSessionLocked(sessionState).notifyAdBuffer(buffer);
+ getSessionLocked(sessionState).notifyAdBufferReady(buffer);
} catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in notifyAdBuffer", e);
}
@@ -3765,7 +3765,7 @@
}
@Override
- public void onTvMessage(String type, Bundle data) {
+ public void onTvMessage(int type, Bundle data) {
synchronized (mLock) {
if (DEBUG) {
Slog.d(TAG, "onTvMessage(type=" + type + ", data=" + data + ")");
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index 61c137e..0d4a76e 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -1117,7 +1117,7 @@
}
@Override
- public void notifyTvMessage(IBinder sessionToken, String type, Bundle data, int userId) {
+ public void notifyTvMessage(IBinder sessionToken, int type, Bundle data, int userId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
@@ -3047,16 +3047,16 @@
}
@Override
- public void onAdBuffer(AdBuffer buffer) {
+ public void onAdBufferReady(AdBuffer buffer) {
synchronized (mLock) {
if (DEBUG) {
- Slogf.d(TAG, "onAdBuffer(buffer=" + buffer + ")");
+ Slogf.d(TAG, "onAdBufferReady(buffer=" + buffer + ")");
}
if (mSessionState.mSession == null || mSessionState.mClient == null) {
return;
}
try {
- mSessionState.mClient.onAdBuffer(buffer, mSessionState.mSeq);
+ mSessionState.mClient.onAdBufferReady(buffer, mSessionState.mSeq);
} catch (RemoteException e) {
Slogf.e(TAG, "error in onAdBuffer", e);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 28b974c..faa0cc9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1734,7 +1734,7 @@
: null;
}
- private void clearLastParentBeforePip() {
+ void clearLastParentBeforePip() {
if (mLastParentBeforePip != null) {
mLastParentBeforePip.mChildPipActivity = null;
mLastParentBeforePip = null;
@@ -2098,7 +2098,7 @@
mTaskSupervisor = supervisor;
info.taskAffinity = computeTaskAffinity(info.taskAffinity, info.applicationInfo.uid,
- launchMode);
+ info.launchMode, mActivityComponent);
taskAffinity = info.taskAffinity;
final String uid = Integer.toString(info.applicationInfo.uid);
if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null
@@ -2199,12 +2199,18 @@
* @param affinity The affinity of the activity.
* @param uid The user-ID that has been assigned to this application.
* @param launchMode The activity launch mode
+ * @param componentName The activity component name. This is only useful when the given
+ * launchMode is {@link ActivityInfo#LAUNCH_SINGLE_INSTANCE}
* @return The task affinity
*/
- static String computeTaskAffinity(String affinity, int uid, int launchMode) {
+ static String computeTaskAffinity(String affinity, int uid, int launchMode,
+ ComponentName componentName) {
final String uidStr = Integer.toString(uid);
if (affinity != null && !affinity.startsWith(uidStr)) {
- affinity = uidStr + (launchMode == LAUNCH_SINGLE_INSTANCE ? "-si:" : ":") + affinity;
+ affinity = uidStr + ":" + affinity;
+ if (launchMode == LAUNCH_SINGLE_INSTANCE && componentName != null) {
+ affinity += ":" + componentName.hashCode();
+ }
}
return affinity;
}
@@ -7824,25 +7830,35 @@
/**
* Returns the requested {@link Configuration.Orientation} for the current activity.
- *
- * <p>When The current orientation is set to {@link SCREEN_ORIENTATION_BEHIND} it returns the
- * requested orientation for the activity below which is the first activity with an explicit
- * (different from {@link SCREEN_ORIENTATION_UNSET}) orientation which is not {@link
- * SCREEN_ORIENTATION_BEHIND}.
*/
@Configuration.Orientation
@Override
int getRequestedConfigurationOrientation(boolean forDisplay) {
+ return getRequestedConfigurationOrientation(forDisplay, getOverrideOrientation());
+ }
+
+ /**
+ * Returns the requested {@link Configuration.Orientation} for the requested
+ * {@link ActivityInfo.ScreenOrientation}.
+ *
+ * <p>When the current screen orientation is set to {@link SCREEN_ORIENTATION_BEHIND} it returns
+ * the requested orientation for the activity below which is the first activity with an explicit
+ * (different from {@link SCREEN_ORIENTATION_UNSET}) orientation which is not {@link
+ * SCREEN_ORIENTATION_BEHIND}.
+ */
+ @Configuration.Orientation
+ int getRequestedConfigurationOrientation(boolean forDisplay,
+ @ActivityInfo.ScreenOrientation int requestedOrientation) {
if (mLetterboxUiController.hasInheritedOrientation()) {
final RootDisplayArea root = getRootDisplayArea();
if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
- return ActivityInfo.reverseOrientation(
+ return reverseConfigurationOrientation(
mLetterboxUiController.getInheritedOrientation());
} else {
return mLetterboxUiController.getInheritedOrientation();
}
}
- if (task != null && getOverrideOrientation() == SCREEN_ORIENTATION_BEHIND) {
+ if (task != null && requestedOrientation == SCREEN_ORIENTATION_BEHIND) {
// We use Task here because we want to be consistent with what happens in
// multi-window mode where other tasks orientations are ignored.
final ActivityRecord belowCandidate = task.getActivity(
@@ -7853,7 +7869,23 @@
return belowCandidate.getRequestedConfigurationOrientation(forDisplay);
}
}
- return super.getRequestedConfigurationOrientation(forDisplay);
+ return super.getRequestedConfigurationOrientation(forDisplay, requestedOrientation);
+ }
+
+ /**
+ * Returns the reversed configuration orientation.
+ * @hide
+ */
+ @Configuration.Orientation
+ public static int reverseConfigurationOrientation(@Configuration.Orientation int orientation) {
+ switch (orientation) {
+ case ORIENTATION_LANDSCAPE:
+ return ORIENTATION_PORTRAIT;
+ case ORIENTATION_PORTRAIT:
+ return ORIENTATION_LANDSCAPE;
+ default:
+ return orientation;
+ }
}
/**
@@ -7899,6 +7931,19 @@
if (mLetterboxUiController.shouldIgnoreRequestedOrientation(requestedOrientation)) {
return;
}
+ // This is necessary in order to avoid going into size compat mode when the orientation
+ // change request comes from the app
+ if (mWmService.mLetterboxConfiguration
+ .isSizeCompatModeDisabledAfterOrientationChangeFromApp()
+ && getRequestedConfigurationOrientation(false, requestedOrientation)
+ != getRequestedConfigurationOrientation(false /*forDisplay */)) {
+ // Do not change the requested configuration now, because this will be done when setting
+ // the orientation below with the new mCompatDisplayInsets
+ clearSizeCompatModeAttributes();
+ }
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Setting requested orientation %s for %s",
+ ActivityInfo.screenOrientationToString(requestedOrientation), this);
setOrientation(requestedOrientation, this);
// Push the new configuration to the requested app in case where it's not pushed, e.g. when
@@ -8139,17 +8184,20 @@
mDisplayContent, this, mLetterboxBoundsForFixedOrientationAndAspectRatio);
}
- @VisibleForTesting
- void clearSizeCompatMode() {
- final float lastSizeCompatScale = mSizeCompatScale;
+ private void clearSizeCompatModeAttributes() {
mInSizeCompatModeForBounds = false;
mSizeCompatScale = 1f;
mSizeCompatBounds = null;
mCompatDisplayInsets = null;
+ }
+
+ @VisibleForTesting
+ void clearSizeCompatMode() {
+ final float lastSizeCompatScale = mSizeCompatScale;
+ clearSizeCompatModeAttributes();
if (mSizeCompatScale != lastSizeCompatScale) {
forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
}
-
// Clear config override in #updateCompatDisplayInsets().
final int activityType = getActivityType();
final Configuration overrideConfig = getRequestedOverrideConfiguration();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 5ea2854..bfb735d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -490,8 +490,7 @@
/** Dump the current activities state. */
public abstract boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
- boolean dumpFocusedRootTaskOnly, boolean dumpVerbose, int displayIdFilter,
- @UserIdInt int userId);
+ boolean dumpFocusedRootTaskOnly, int displayIdFilter, @UserIdInt int userId);
/** Dump the current state for inclusion in oom dump. */
public abstract void dumpForOom(PrintWriter pw);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 898b477..992743a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4205,8 +4205,7 @@
*/
protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
- boolean dumpFocusedRootTaskOnly, boolean dumpVerbose, int displayIdFilter,
- @UserIdInt int userId) {
+ boolean dumpFocusedRootTaskOnly, int displayIdFilter, @UserIdInt int userId) {
ArrayList<ActivityRecord> activities;
synchronized (mGlobalLock) {
@@ -4251,7 +4250,7 @@
}
}
}
- dumpActivity(" ", fd, pw, activities.get(i), newArgs, dumpAll, dumpVerbose);
+ dumpActivity(" ", fd, pw, activities.get(i), newArgs, dumpAll);
}
if (!printedAnything) {
// Typically happpens when no task matches displayIdFilter
@@ -4265,7 +4264,7 @@
* there is a thread associated with the activity.
*/
private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
- ActivityRecord r, String[] args, boolean dumpAll, boolean dumpVerbose) {
+ ActivityRecord r, String[] args, boolean dumpAll) {
String innerPrefix = prefix + " ";
IApplicationThread appThread = null;
synchronized (mGlobalLock) {
@@ -4281,15 +4280,11 @@
} else {
pw.print("(not running)");
}
- if (dumpVerbose) {
- pw.print(" userId=");
- pw.print(r.mUserId);
- pw.print(" uid=");
- pw.print(r.getUid());
- printDisplayInfoAndNewLine(pw, r);
- } else {
- pw.println();
- }
+ pw.print(" userId=");
+ pw.print(r.mUserId);
+ pw.print(" uid=");
+ pw.print(r.getUid());
+ printDisplayInfoAndNewLine(pw, r);
if (dumpAll) {
r.dump(pw, innerPrefix, /* dumpAll= */ true);
}
@@ -6618,11 +6613,10 @@
@Override
public boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
- boolean dumpFocusedRootTaskOnly, boolean dumpVerbose, int displayIdFilter,
+ boolean dumpFocusedRootTaskOnly, int displayIdFilter,
@UserIdInt int userId) {
return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti, dumpAll,
- dumpVisibleRootTasksOnly, dumpFocusedRootTaskOnly, dumpVerbose, displayIdFilter,
- userId);
+ dumpVisibleRootTasksOnly, dumpFocusedRootTaskOnly, displayIdFilter, userId);
}
@Override
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index 5e44d6c..e91c9d4 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -41,7 +41,8 @@
// Desktop mode feature flag.
static final boolean DESKTOP_MODE_SUPPORTED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+ "persist.wm.debug.desktop_mode", false) || SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_mode_2", false);
// Override default freeform task width when desktop mode is enabled. In dips.
private static final int DESKTOP_MODE_DEFAULT_WIDTH_DP = SystemProperties.getInt(
"persist.wm.debug.desktop_mode.default_width", 840);
@@ -79,8 +80,8 @@
appendLog("not in bounds phase, skipping");
return RESULT_SKIP;
}
- if (!task.inFreeformWindowingMode()) {
- appendLog("not a freeform task, skipping");
+ if (!task.isActivityTypeStandard()) {
+ appendLog("not standard activity type, skipping");
return RESULT_SKIP;
}
if (!currentParams.mBounds.isEmpty()) {
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 594929b..244656c 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -49,7 +49,7 @@
30066 wm_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
# Keyguard status changed
-30067 wm_set_keyguard_shown (Display Id|1|5),(keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(Reason|3)
+30067 wm_set_keyguard_shown (Display Id|1|5),(keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(occluded|1),(Reason|3)
# Out of memory for surfaces.
31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3)
@@ -65,6 +65,8 @@
# bootanim finished:
31007 wm_boot_animation_done (time|2|3)
+# Notify keyguard occlude statuc change to SysUI.
+31008 wm_set_keyguard_occluded (occluded|1),(animate|1),(transit|1),(Channel|3)
# Back navigation.
31100 wm_back_navi_canceled (Reason|3)
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 32b3ccf..08a6358 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -188,6 +188,7 @@
keyguardShowing ? 1 : 0,
aodShowing ? 1 : 0,
state.mKeyguardGoingAway ? 1 : 0,
+ state.mOccluded ? 1 : 0,
"setKeyguardShown");
// Update the task snapshot if the screen will not be turned off. To make sure that the
@@ -253,9 +254,10 @@
try {
EventLogTags.writeWmSetKeyguardShown(
displayId,
- 1 /* keyguardShowing */,
+ state.mKeyguardShowing ? 1 : 0,
state.mAodShowing ? 1 : 0,
1 /* keyguardGoingAway */,
+ state.mOccluded ? 1 : 0,
"keyguardGoingAway");
final int transitFlags = convertTransitFlags(flags);
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
@@ -669,6 +671,15 @@
boolean hasChange = false;
if (lastOccluded != mOccluded) {
+ if (mDisplayId == DEFAULT_DISPLAY) {
+ EventLogTags.writeWmSetKeyguardShown(
+ mDisplayId,
+ mKeyguardShowing ? 1 : 0,
+ mAodShowing ? 1 : 0,
+ mKeyguardGoingAway ? 1 : 0,
+ mOccluded ? 1 : 0,
+ "updateVisibility");
+ }
controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
hasChange = true;
} else if (!lastKeyguardGoingAway && mKeyguardGoingAway) {
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index fa49a6ba..37cf5bc 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -19,6 +19,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ALLOW_IGNORE_ORIENTATION_REQUEST;
+import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP;
import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_CAMERA_COMPAT_TREATMENT;
import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_COMPAT_FAKE_FOCUS;
import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY;
@@ -314,6 +315,10 @@
mDeviceConfig.updateFlagActiveStatus(
/* isActive */ mTranslucentLetterboxingEnabled,
/* key */ KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY);
+ mDeviceConfig.updateFlagActiveStatus(
+ /* isActive */ true,
+ /* key */ KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP);
+
mLetterboxConfigurationPersister = letterboxConfigurationPersister;
mLetterboxConfigurationPersister.start();
}
@@ -327,6 +332,16 @@
}
/**
+ * Whether size compat mode is disabled after an orientation change request comes from the app.
+ * This value is controlled via {@link android.provider.DeviceConfig}.
+ */
+ // TODO(b/270356567) Clean up this flag
+ boolean isSizeCompatModeDisabledAfterOrientationChangeFromApp() {
+ return mDeviceConfig.getFlag(
+ KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP);
+ }
+
+ /**
* Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
* #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
index df3c8f0..1651af3 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
@@ -20,7 +20,6 @@
import android.provider.DeviceConfig;
import android.util.ArraySet;
-
import com.android.internal.annotations.VisibleForTesting;
import java.util.Map;
@@ -53,6 +52,11 @@
private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = true;
+ static final String KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP =
+ "disable_size_compat_mode_after_orientation_change_from_app";
+ private static final boolean
+ DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP = true;
+
@VisibleForTesting
static final Map<String, Boolean> sKeyToDefaultValueMap = Map.of(
KEY_ENABLE_CAMERA_COMPAT_TREATMENT,
@@ -64,7 +68,9 @@
KEY_ENABLE_COMPAT_FAKE_FOCUS,
DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS,
KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY,
- DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY
+ DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY,
+ KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP,
+ DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP
);
// Whether camera compatibility treatment is enabled.
@@ -93,6 +99,10 @@
private boolean mIsTranslucentLetterboxingAllowed =
DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY;
+ // Whether size compat mode is disabled after an orientation change request comes from the app
+ private boolean mIsSizeCompatModeDisabledAfterOrientationChangeFromApp =
+ DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP;
+
// Set of active device configs that need to be updated in
// DeviceConfig.OnPropertiesChangedListener#onPropertiesChanged.
private final ArraySet<String> mActiveDeviceConfigsSet = new ArraySet<>();
@@ -142,6 +152,8 @@
return mIsCompatFakeFocusAllowed;
case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY:
return mIsTranslucentLetterboxingAllowed;
+ case KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP:
+ return mIsSizeCompatModeDisabledAfterOrientationChangeFromApp;
default:
throw new AssertionError("Unexpected flag name: " + key);
}
@@ -169,6 +181,10 @@
case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY:
mIsTranslucentLetterboxingAllowed = getDeviceConfig(key, defaultValue);
break;
+ case KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP:
+ mIsSizeCompatModeDisabledAfterOrientationChangeFromApp =
+ getDeviceConfig(key, defaultValue);
+ break;
default:
throw new AssertionError("Unexpected flag name: " + key);
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 7208934..f355f08 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1395,9 +1395,9 @@
FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
mActivityRecord /* boundary */, false /* includeBoundary */,
true /* traverseTopToBottom */);
- if (firstOpaqueActivityBeneath == null) {
+ if (firstOpaqueActivityBeneath == null || firstOpaqueActivityBeneath.isEmbedded()) {
// We skip letterboxing if the translucent activity doesn't have any opaque
- // activities beneath
+ // activities beneath or the activity below is embedded which never has letterbox.
return;
}
inheritConfiguration(firstOpaqueActivityBeneath);
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 14b845c..cb94146 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -37,6 +37,7 @@
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
@@ -1221,7 +1222,7 @@
void onActivityIdle(ActivityRecord r) {
// Clean up the hidden tasks when going to home because the user may not be unable to return
// to the task from recents.
- if (!mHiddenTasks.isEmpty() && r.isActivityTypeHome()) {
+ if (!mHiddenTasks.isEmpty() && r.isActivityTypeHome() && r.isState(RESUMED)) {
removeUnreachableHiddenTasks(r.getWindowingMode());
}
if (mCheckTrimmableTasksOnIdle) {
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 3635ebb..b879d1a 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
@@ -195,6 +197,13 @@
+ " transit=%s, apps=%d, wallpapers=%d, nonApps=%d",
AppTransition.appTransitionOldToString(transit), appTargets.length,
wallpaperTargets.length, nonAppTargets.length);
+ if (AppTransition.isKeyguardOccludeTransitOld(transit)) {
+ EventLogTags.writeWmSetKeyguardOccluded(
+ transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE ? 0 : 1,
+ 1 /* animate */,
+ transit,
+ "onAnimationStart");
+ }
mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
wallpaperTargets, nonAppTargets, mFinishedCallback);
} catch (RemoteException e) {
@@ -356,6 +365,11 @@
final boolean isKeyguardOccluded = mDisplayContent.isKeyguardOccluded();
try {
+ EventLogTags.writeWmSetKeyguardOccluded(
+ isKeyguardOccluded ? 1 : 0,
+ 0 /* animate */,
+ 0 /* transit */,
+ "onAnimationCancelled");
mRemoteAnimationAdapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to notify cancel", e);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3680e6d..ebdc537 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2584,6 +2584,9 @@
EventLogTags.writeWmTaskRemoved(mTaskId, getRootTaskId(), getDisplayId(), reason);
clearPinnedTaskIfNeed();
+ if (mChildPipActivity != null) {
+ mChildPipActivity.clearLastParentBeforePip();
+ }
// If applicable let the TaskOrganizer know the Task is vanishing.
setTaskOrganizer(null);
@@ -5329,7 +5332,7 @@
// the task if the affinity has changed.
final String affinity = ActivityRecord.computeTaskAffinity(destAffinity, srec.getUid(),
- srec.launchMode);
+ srec.launchMode, srec.mActivityComponent);
if (srec == null || srec.getTask().affinity == null
|| !srec.getTask().affinity.equals(affinity)) {
return true;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 132f5a7..d42a629 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1493,7 +1493,26 @@
*/
@Configuration.Orientation
int getRequestedConfigurationOrientation(boolean forDisplay) {
- int requestedOrientation = getOverrideOrientation();
+ return getRequestedConfigurationOrientation(forDisplay, getOverrideOrientation());
+ }
+
+ /**
+ * Gets the configuration orientation by the requested screen orientation
+ *
+ * @param forDisplay whether it is the requested config orientation for display.
+ * If {@code true}, we may reverse the requested orientation if the root is
+ * different from the display, so that when the display rotates to the
+ * reversed orientation, the requested app will be in the requested
+ * orientation.
+ * @param requestedOrientation the screen orientation({@link ScreenOrientation}) that is
+ * requested
+ * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
+ * {@link Configuration#ORIENTATION_PORTRAIT},
+ * {@link Configuration#ORIENTATION_UNDEFINED}).
+ */
+ @Configuration.Orientation
+ int getRequestedConfigurationOrientation(boolean forDisplay,
+ @ScreenOrientation int requestedOrientation) {
final RootDisplayArea root = getRootDisplayArea();
if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
// Reverse the requested orientation if the orientation of its root is different from
@@ -1503,7 +1522,7 @@
// (portrait).
// When an app below the DAG is requesting landscape, it should actually request the
// display to be portrait, so that the DAG and the app will be in landscape.
- requestedOrientation = reverseOrientation(getOverrideOrientation());
+ requestedOrientation = reverseOrientation(requestedOrientation);
}
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cf0fc09..f86b997 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4523,7 +4523,7 @@
void requestUpdateWallpaperIfNeeded() {
final DisplayContent dc = getDisplayContent();
- if (dc != null && hasWallpaper()) {
+ if (dc != null && ((mIsWallpaper && !mLastConfigReportedToClient) || hasWallpaper())) {
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
dc.setLayoutNeeded();
mWmService.mWindowPlacerLocked.requestTraversal();
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index e84f0cc..94260e20 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -46,9 +46,11 @@
public ClearRequestSession(Context context, int userId, int callingUid,
IClearCredentialStateCallback callback, ClearCredentialStateRequest request,
- CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) {
+ CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal,
+ long startedTimestamp) {
super(context, userId, callingUid, request, callback, RequestInfo.TYPE_UNDEFINED,
- callingAppInfo, cancellationSignal);
+ callingAppInfo, cancellationSignal, startedTimestamp);
+ setupInitialPhaseMetric(ApiName.CLEAR_CREDENTIAL.getMetricCode(), MetricUtilities.ZERO);
}
/**
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 7e1780d..47b8c7d 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -53,9 +53,11 @@
CreateCredentialRequest request,
ICreateCredentialCallback callback,
CallingAppInfo callingAppInfo,
- CancellationSignal cancellationSignal) {
+ CancellationSignal cancellationSignal,
+ long startedTimestamp) {
super(context, userId, callingUid, request, callback, RequestInfo.TYPE_CREATE,
- callingAppInfo, cancellationSignal);
+ callingAppInfo, cancellationSignal, startedTimestamp);
+ setupInitialPhaseMetric(ApiName.CREATE_CREDENTIAL.getMetricCode(), MetricUtilities.UNIT);
}
/**
diff --git a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
index 14c49b3..8ccc61b 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
@@ -25,17 +25,19 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
/** Contains information on what CredentialProvider has what provisioned Credential. */
public class CredentialDescriptionRegistry {
+ private static final String FLAT_STRING_SPLIT_REGEX = ";";
private static final int MAX_ALLOWED_CREDENTIAL_DESCRIPTIONS = 128;
private static final int MAX_ALLOWED_ENTRIES_PER_PROVIDER = 16;
@GuardedBy("sLock")
@@ -164,14 +166,16 @@
/** Returns package names and entries of a CredentialProviders that can satisfy a given
* {@link CredentialDescription}. */
public Set<FilterResult> getFilteredResultForProvider(String packageName,
- String flatRequestStrings) {
+ String flatRequestString) {
Set<FilterResult> result = new HashSet<>();
if (!mCredentialDescriptions.containsKey(packageName)) {
return result;
}
Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
+ Set<String> unflattenedRequestString = flatStringToSet(flatRequestString);
for (CredentialDescription containedDescription: currentSet) {
- if (flatRequestStrings.equals(containedDescription.getFlattenedRequestString())) {
+ if (checkForMatch(flatStringToSet(containedDescription.getFlattenedRequestString()),
+ unflattenedRequestString)) {
result.add(new FilterResult(packageName,
containedDescription.getFlattenedRequestString(), containedDescription
.getCredentialEntries()));
@@ -182,12 +186,16 @@
/** Returns package names of CredentialProviders that can satisfy a given
* {@link CredentialDescription}. */
- public Set<FilterResult> getMatchingProviders(Set<String> flatRequestString) {
+ public Set<FilterResult> getMatchingProviders(Set<String> flatRequestStrings) {
Set<FilterResult> result = new HashSet<>();
+ Set<Set<String>> unflattenedRequestStrings = flatRequestStrings.stream().map(
+ CredentialDescriptionRegistry::flatStringToSet).collect(Collectors.toSet());
for (String packageName: mCredentialDescriptions.keySet()) {
Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
for (CredentialDescription containedDescription : currentSet) {
- if (flatRequestString.contains(containedDescription.getFlattenedRequestString())) {
+ if (canProviderSatisfyAny(flatStringToSet(containedDescription
+ .getFlattenedRequestString()),
+ unflattenedRequestStrings)) {
result.add(new FilterResult(packageName,
containedDescription.getFlattenedRequestString(), containedDescription
.getCredentialEntries()));
@@ -203,4 +211,24 @@
}
}
+ private static boolean canProviderSatisfyAny(Set<String> registeredUnflattenedStrings,
+ Set<Set<String>> requestedUnflattenedStrings) {
+ for (Set<String> requestedUnflattenedString : requestedUnflattenedStrings) {
+ if (registeredUnflattenedStrings.containsAll(requestedUnflattenedString)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static boolean checkForMatch(Set<String> registeredUnflattenedStrings,
+ Set<String> requestedUnflattenedString) {
+ return registeredUnflattenedStrings.containsAll(requestedUnflattenedString);
+ }
+
+ static Set<String> flatStringToSet(String flatString) {
+ return new HashSet<>(Arrays
+ .asList(flatString.split(FLAT_STRING_SPLIT_REGEX)));
+ }
+
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 6e998c4..10d3dc0 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -29,11 +29,9 @@
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialException;
import android.credentials.CreateCredentialRequest;
-import android.credentials.CredentialDescription;
import android.credentials.CredentialManager;
import android.credentials.CredentialOption;
import android.credentials.CredentialProviderInfo;
@@ -230,8 +228,10 @@
if (hasPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)) {
return;
}
-
- throw new SecurityException("Caller is missing permission: QUERY_ALL_PACKAGES or LIST_ENABLED_CREDENTIAL_PROVIDERS");
+
+ throw new SecurityException(
+ "Caller is missing permission: QUERY_ALL_PACKAGES or "
+ + "LIST_ENABLED_CREDENTIAL_PROVIDERS");
}
private boolean hasPermission(String permission) {
@@ -291,19 +291,15 @@
List<ProviderSession> providerSessions = new ArrayList<>();
for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result :
activeCredentialContainers) {
- providerSessions.add(
- ProviderRegistryGetSession.createNewSession(
- mContext,
- UserHandle.getCallingUserId(),
- session,
- CredentialProviderInfoFactory.getCredentialProviderFromPackageName(
- mContext, UserHandle.getCallingUserId() ,
- result.second.mPackageName,
- CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS,
- new HashSet<>()),
- session.mClientAppInfo,
- result.second.mPackageName,
- result.first));
+ ProviderSession providerSession = ProviderRegistryGetSession.createNewSession(
+ mContext,
+ UserHandle.getCallingUserId(),
+ session,
+ session.mClientAppInfo,
+ result.second.mPackageName,
+ result.first);
+ providerSessions.add(providerSession);
+ session.addProviderSession(providerSession.getComponentName(), providerSession);
}
return providerSessions;
}
@@ -333,10 +329,15 @@
new HashSet<>();
for (CredentialDescriptionRegistry.FilterResult filterResult : filterResults) {
+ Set<String> registeredUnflattenedStrings = CredentialDescriptionRegistry
+ .flatStringToSet(filterResult.mFlattenedRequest);
for (CredentialOption credentialOption : options) {
- if (filterResult.mFlattenedRequest.equals(credentialOption
- .getCredentialRetrievalData()
- .getString(CredentialOption.FLATTENED_REQUEST))) {
+ Set<String> requestedUnflattenedStrings = CredentialDescriptionRegistry
+ .flatStringToSet(credentialOption
+ .getCredentialRetrievalData()
+ .getString(CredentialOption.FLATTENED_REQUEST));
+ if (CredentialDescriptionRegistry.checkForMatch(registeredUnflattenedStrings,
+ requestedUnflattenedStrings)) {
result.add(new Pair<>(credentialOption, filterResult));
}
}
@@ -408,6 +409,7 @@
GetCredentialRequest request,
IGetCredentialCallback callback,
final String callingPackage) {
+ final long timestampBegan = System.nanoTime();
Log.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
@@ -429,7 +431,8 @@
callback,
request,
constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
- CancellationSignal.fromTransport(cancelTransport));
+ CancellationSignal.fromTransport(cancelTransport),
+ timestampBegan);
processGetCredential(request, callback, session);
return cancelTransport;
@@ -508,6 +511,9 @@
+ e.getMessage());
}
}
+
+ finalizeAndEmitInitialPhaseMetric(session);
+ // TODO(b/271135048) - May still be worth emitting in the empty cases above.
providerSessions.forEach(ProviderSession::invokeSession);
}
@@ -516,6 +522,7 @@
CreateCredentialRequest request,
ICreateCredentialCallback callback,
String callingPackage) {
+ final long timestampBegan = System.nanoTime();
Log.i(TAG, "starting executeCreateCredential with callingPackage: "
+ callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
@@ -538,7 +545,8 @@
request,
callback,
constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
- CancellationSignal.fromTransport(cancelTransport));
+ CancellationSignal.fromTransport(cancelTransport),
+ timestampBegan);
processCreateCredential(request, callback, session);
return cancelTransport;
@@ -566,10 +574,17 @@
}
}
+ finalizeAndEmitInitialPhaseMetric(session);
// Iterate over all provider sessions and invoke the request
providerSessions.forEach(ProviderSession::invokeSession);
}
+ private void finalizeAndEmitInitialPhaseMetric(RequestSession session) {
+ var initMetric = session.mInitialPhaseMetric;
+ initMetric.setCredentialServiceBeginQueryTimeNanoseconds(System.nanoTime());
+ MetricUtilities.logApiCalled(initMetric);
+ }
+
@Override
public void setEnabledProviders(
List<String> providers, int userId, ISetEnabledProvidersCallback callback) {
@@ -654,6 +669,7 @@
}
MetricUtilities.logApiCalled(ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
ApiStatus.SUCCESS, callingUid);
+ // TODO(b/271135048) - Update asap to use the new logging types
return true;
}
}
@@ -683,12 +699,22 @@
mContext, userId, providerFilter, getEnabledProviders());
}
- private Set<ServiceInfo> getEnabledProviders() {
- Set<ServiceInfo> enabledProviders = new HashSet<>();
+ @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
+ // this.mLock
+ private Set<ComponentName> getEnabledProviders() {
+ Set<ComponentName> enabledProviders = new HashSet<>();
synchronized (mLock) {
runForUser(
(service) -> {
- enabledProviders.add(service.getCredentialProviderInfo().getServiceInfo());
+ try {
+ enabledProviders.add(
+ service.getCredentialProviderInfo()
+ .getServiceInfo().getComponentName());
+ } catch (NullPointerException e) {
+ // Safe check
+ Log.i(TAG, "Skipping provider as either the providerInfo"
+ + "or serviceInfo is null - weird");
+ }
});
}
return enabledProviders;
@@ -699,6 +725,7 @@
ClearCredentialStateRequest request,
IClearCredentialStateCallback callback,
String callingPackage) {
+ final long timestampBegan = System.nanoTime();
Log.i(TAG, "starting clearCredentialState with callingPackage: " + callingPackage);
final int userId = UserHandle.getCallingUserId();
int callingUid = Binder.getCallingUid();
@@ -716,7 +743,8 @@
callback,
request,
constructCallingAppInfo(callingPackage, userId, null),
- CancellationSignal.fromTransport(cancelTransport));
+ CancellationSignal.fromTransport(cancelTransport),
+ timestampBegan);
// Initiate all provider sessions
// TODO: Determine if provider needs to have clear capability in their manifest
@@ -735,6 +763,8 @@
}
}
+ finalizeAndEmitInitialPhaseMetric(session);
+
// Iterate over all provider sessions and invoke the request
providerSessions.forEach(ProviderSession::invokeSession);
return cancelTransport;
@@ -752,44 +782,6 @@
enforceCallingPackage(callingPackage, Binder.getCallingUid());
- List<CredentialProviderInfo> services =
- getServicesForCredentialDescription(UserHandle.getCallingUserId());
-
- List<String> providers =
- services.stream()
- .map(
- credentialProviderInfo ->
- credentialProviderInfo.getServiceInfo().packageName)
- .toList();
-
- if (!providers.contains(callingPackage)) {
- throw new NonCredentialProviderCallerException(callingPackage);
- }
-
- List<CredentialProviderInfo> matchingService =
- services.stream()
- .filter(
- credentialProviderInfo ->
- credentialProviderInfo
- .getServiceInfo()
- .packageName
- .equals(callingPackage))
- .toList();
-
- CredentialProviderInfo credentialProviderInfo = matchingService.get(0);
-
- Set<String> supportedTypes =
- request.getCredentialDescriptions().stream()
- .map(CredentialDescription::getType)
- .filter(credentialProviderInfo::hasCapability)
- .collect(Collectors.toSet());
-
- if (supportedTypes.size() != request.getCredentialDescriptions().size()) {
- throw new IllegalArgumentException(
- "CredentialProvider does not support one or more"
- + "of the registered types. Check your XML entry.");
- }
-
CredentialDescriptionRegistry session =
CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
@@ -808,20 +800,6 @@
enforceCallingPackage(callingPackage, Binder.getCallingUid());
- List<CredentialProviderInfo> services =
- getServicesForCredentialDescription(UserHandle.getCallingUserId());
-
- List<String> providers =
- services.stream()
- .map(
- credentialProviderInfo ->
- credentialProviderInfo.getServiceInfo().packageName)
- .toList();
-
- if (!providers.contains(callingPackage)) {
- throw new NonCredentialProviderCallerException(callingPackage);
- }
-
CredentialDescriptionRegistry session =
CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index f59b32c..8e90c09 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.credentials.CredentialOption;
import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
@@ -36,6 +37,7 @@
import com.android.server.credentials.metrics.ProviderStatusForMetrics;
import java.util.ArrayList;
+import java.util.stream.Collectors;
/**
* Central session for a single getCredentials request. This class listens to the
@@ -47,9 +49,14 @@
private static final String TAG = "GetRequestSession";
public GetRequestSession(Context context, int userId, int callingUid,
IGetCredentialCallback callback, GetCredentialRequest request,
- CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) {
+ CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal,
+ long startedTimestamp) {
super(context, userId, callingUid, request, callback, RequestInfo.TYPE_GET,
- callingAppInfo, cancellationSignal);
+ callingAppInfo, cancellationSignal, startedTimestamp);
+ int numTypes = (request.getCredentialOptions().stream()
+ .map(CredentialOption::getType).collect(
+ Collectors.toSet())).size(); // Dedupe type strings
+ setupInitialPhaseMetric(ApiName.GET_CREDENTIAL.getMetricCode(), numTypes);
}
/**
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 255b2f8..1b3e37a 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -24,8 +24,9 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
-import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.CandidatePhaseMetric;
import com.android.server.credentials.metrics.ChosenProviderMetric;
+import com.android.server.credentials.metrics.InitialPhaseMetric;
import java.util.Map;
@@ -39,7 +40,10 @@
public static final int DEFAULT_INT_32 = -1;
public static final int[] DEFAULT_REPEATED_INT_32 = new int[0];
-
+ // Used for single count metric emits, such as singular amounts of various types
+ public static final int UNIT = 1;
+ // Used for zero count metric emits, such as zero amounts of various types
+ public static final int ZERO = 0;
/**
* This retrieves the uid of any package name, given a context and a component name for the
@@ -98,7 +102,7 @@
int[] candidateStatusList = new int[providerSize];
int index = 0;
for (var session : providerSessions) {
- CandidateProviderMetric metric = session.mCandidateProviderMetric;
+ CandidatePhaseMetric metric = session.mCandidateProviderMetric;
candidateUidList[index] = metric.getCandidateUid();
candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMicroseconds();
candidateStatusList[index] = metric.getProviderQueryStatus();
@@ -155,4 +159,18 @@
}
}
+ /**
+ * Handles the metric emit for the initial phase.
+ *
+ * @param initialPhaseMetric contains all the data for this emit
+ */
+ protected static void logApiCalled(InitialPhaseMetric initialPhaseMetric) {
+ /*
+ FrameworkStatsLog.write(FrameworkStatsLog.INITIAL_PHASE,
+ .. session_id .. initialPhaseMetric.getSessionId(),
+ ...
+ TODO Immediately - Fill in asap now that the split atom is checked in.
+ */
+ }
+
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index b7a4cd5..ab29acc 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -72,7 +72,8 @@
ProviderInternalCallback callbacks,
int userId, RemoteCredentialService remoteCredentialService,
ClearCredentialStateRequest providerRequest) {
- super(context, info, providerRequest, callbacks, userId, remoteCredentialService);
+ super(context, providerRequest, callbacks, info.getComponentName(),
+ userId, remoteCredentialService);
setStatus(Status.PENDING);
}
@@ -95,7 +96,7 @@
/** Called when provider service dies. */
@Override // Callback from the remote provider
public void onProviderServiceDied(RemoteCredentialService service) {
- if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+ if (service.getComponentName().equals(mComponentName)) {
updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
} else {
Slog.i(TAG, "Component names different in onProviderServiceDied - "
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 640cc33..8c9c6cf 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -133,7 +133,7 @@
@NonNull BeginCreateCredentialRequest beginCreateRequest,
@NonNull CreateCredentialRequest completeCreateRequest,
String hybridService) {
- super(context, info, beginCreateRequest, callbacks, userId,
+ super(context, beginCreateRequest, callbacks, info.getComponentName(), userId,
remoteCredentialService);
mCompleteRequest = completeCreateRequest;
setStatus(Status.PENDING);
@@ -161,7 +161,7 @@
/** Called when provider service dies. */
@Override
public void onProviderServiceDied(RemoteCredentialService service) {
- if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+ if (service.getComponentName().equals(mComponentName)) {
updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
} else {
Slog.i(TAG, "Component names different in onProviderServiceDied - "
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 07e2f87..851ccb3 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -169,7 +169,8 @@
CallingAppInfo callingAppInfo,
Map<String, CredentialOption> beginGetOptionToCredentialOptionMap,
String hybridService) {
- super(context, info, beginGetRequest, callbacks, userId, remoteCredentialService);
+ super(context, beginGetRequest, callbacks, info.getComponentName() ,
+ userId, remoteCredentialService);
mCompleteRequest = completeGetRequest;
mCallingAppInfo = callingAppInfo;
setStatus(Status.PENDING);
@@ -196,7 +197,7 @@
/** Called when provider service dies. */
@Override // Callback from the remote provider
public void onProviderServiceDied(RemoteCredentialService service) {
- if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+ if (service.getComponentName().equals(mComponentName)) {
updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
} else {
Slog.i(TAG, "Component names different in onProviderServiceDied - "
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index 457806d..a5196c5 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -19,10 +19,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.credentials.CredentialOption;
-import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialResponse;
import android.credentials.ui.Entry;
@@ -64,7 +64,6 @@
@NonNull Context context,
@UserIdInt int userId,
@NonNull GetRequestSession getRequestSession,
- @NonNull CredentialProviderInfo credentialProviderInfo,
@NonNull CallingAppInfo callingAppInfo,
@NonNull String credentialProviderPackageName,
@NonNull CredentialOption requestOption) {
@@ -72,7 +71,6 @@
context,
userId,
getRequestSession,
- credentialProviderInfo,
callingAppInfo,
credentialProviderPackageName,
requestOption);
@@ -94,11 +92,12 @@
protected ProviderRegistryGetSession(@NonNull Context context,
@NonNull int userId,
@NonNull GetRequestSession session,
- @NonNull CredentialProviderInfo credentialProviderInfo,
@NonNull CallingAppInfo callingAppInfo,
@NonNull String servicePackageName,
@NonNull CredentialOption requestOption) {
- super(context, credentialProviderInfo, requestOption, session, userId, null);
+ super(context, requestOption, session,
+ new ComponentName(servicePackageName, servicePackageName) ,
+ userId, null);
mCredentialDescriptionRegistry = CredentialDescriptionRegistry.forUser(userId);
mCallingAppInfo = callingAppInfo;
mCredentialProviderPackageName = servicePackageName;
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index a8b9bf6..03e2a32 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -31,7 +31,7 @@
import android.os.RemoteException;
import android.util.Log;
-import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.CandidatePhaseMetric;
import com.android.server.credentials.metrics.ProviderStatusForMetrics;
import java.util.UUID;
@@ -59,7 +59,7 @@
@Nullable protected R mProviderResponse;
@NonNull protected Boolean mProviderResponseSet = false;
// Specific candidate provider metric for the provider this session handles
- @Nullable protected CandidateProviderMetric mCandidateProviderMetric;
+ @Nullable protected CandidatePhaseMetric mCandidateProviderMetric;
@NonNull private int mProviderSessionUid;
/**
@@ -114,19 +114,20 @@
@Nullable String message);
}
- protected ProviderSession(@NonNull Context context, @Nullable CredentialProviderInfo info,
+ protected ProviderSession(@NonNull Context context,
@NonNull T providerRequest,
@Nullable ProviderInternalCallback callbacks,
+ @NonNull ComponentName componentName,
@NonNull int userId,
@Nullable RemoteCredentialService remoteCredentialService) {
mContext = context;
- mProviderInfo = info;
+ mProviderInfo = null;
mProviderRequest = providerRequest;
mCallbacks = callbacks;
mUserId = userId;
- mComponentName = info.getServiceInfo().getComponentName();
+ mComponentName = componentName;
mRemoteCredentialService = remoteCredentialService;
- mCandidateProviderMetric = new CandidateProviderMetric();
+ mCandidateProviderMetric = new CandidatePhaseMetric();
mProviderSessionUid = MetricUtilities.getPackageUid(mContext, mComponentName);
}
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index c1f35d0..edddba0 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -36,8 +36,9 @@
import com.android.internal.R;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
-import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.CandidatePhaseMetric;
import com.android.server.credentials.metrics.ChosenProviderMetric;
+import com.android.server.credentials.metrics.InitialPhaseMetric;
import java.util.ArrayList;
import java.util.HashMap;
@@ -75,7 +76,7 @@
protected final Map<String, ProviderSession> mProviders = new HashMap<>();
protected ChosenProviderMetric mChosenProviderMetric = new ChosenProviderMetric();
- //TODO improve design to allow grouped metrics per request
+ protected InitialPhaseMetric mInitialPhaseMetric = new InitialPhaseMetric();
protected final String mHybridService;
@NonNull
@@ -96,7 +97,7 @@
@UserIdInt int userId, int callingUid, @NonNull T clientRequest, U clientCallback,
@NonNull String requestType,
CallingAppInfo callingAppInfo,
- CancellationSignal cancellationSignal) {
+ CancellationSignal cancellationSignal, long timestampStarted) {
mContext = context;
mUserId = userId;
mCallingUid = callingUid;
@@ -111,6 +112,9 @@
mUserId, this);
mHybridService = context.getResources().getString(
R.string.config_defaultCredentialManagerHybridService);
+ mInitialPhaseMetric.setCredentialServiceStartedTimeNanoseconds(timestampStarted);
+ mInitialPhaseMetric.setSessionId(mRequestId.hashCode());
+ mInitialPhaseMetric.setCallerUid(mCallingUid);
}
public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
@@ -118,6 +122,16 @@
protected abstract void launchUiWithProviderData(ArrayList<ProviderData> providerDataList);
+ // Sets up the initial metric collector for use across all request session impls
+ protected void setupInitialPhaseMetric(int metricCode, int requestClassType) {
+ this.mInitialPhaseMetric.setApiName(metricCode);
+ this.mInitialPhaseMetric.setCountRequestClassType(requestClassType);
+ }
+
+ public void addProviderSession(ComponentName componentName, ProviderSession providerSession) {
+ mProviders.put(componentName.flattenToString(), providerSession);
+ }
+
// UI callbacks
@Override // from CredentialManagerUiCallbacks
@@ -218,7 +232,7 @@
* @param componentName the componentName to associate with a provider
*/
protected void setChosenMetric(ComponentName componentName) {
- CandidateProviderMetric metric = this.mProviders.get(componentName.flattenToString())
+ CandidatePhaseMetric metric = this.mProviders.get(componentName.flattenToString())
.mCandidateProviderMetric;
mChosenProviderMetric.setChosenUid(metric.getCandidateUid());
mChosenProviderMetric.setFinalFinishTimeNanoseconds(System.nanoTime());
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
new file mode 100644
index 0000000..1c7fb69
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
@@ -0,0 +1,246 @@
+/*
+ * 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.credentials.metrics;
+
+import android.util.Log;
+
+import com.android.server.credentials.MetricUtilities;
+
+/**
+ * The central candidate provider metric object that mimics our defined metric setup.
+ * Some types are redundant across these metric collectors, but that has debug use-cases as
+ * these data-types are available at different moments of the flow (and typically, one can feed
+ * into the next).
+ * TODO(b/270403549) - iterate on this in V3+
+ */
+public class CandidatePhaseMetric {
+
+ private static final String TAG = "CandidateProviderMetric";
+ // Since this will always be the second in the split sequence, this is statically 2
+ private static final int SESSION_ID = 2;
+ // The sequence number of this emit of the API call, default -1, equal for all candidates
+ private int mSequenceId = -1;
+ // Indicates if this provider returned from the query phase, default false
+ private boolean mQueryReturned = false;
+
+ // The candidate provider uid
+ private int mCandidateUid = -1;
+
+ // Raw timestamp in nanoseconds, will be converted to microseconds for logging
+
+ //For reference, the initial log timestamp when the service started running the API call
+ private long mServiceBeganTimeNanoseconds = -1;
+ // The moment when the query phase began
+ private long mStartQueryTimeNanoseconds = -1;
+ // The moment when the query phase ended
+ private long mQueryFinishTimeNanoseconds = -1;
+
+ // The status of this particular provider
+ private int mProviderQueryStatus = -1;
+ // Indicates if an exception was thrown by this provider, false by default
+ private boolean mHasException = false;
+ // Indicates the number of total entries available. We can also locally store the entries, but
+ // cannot emit them in the current split form. TODO(b/271135048) - possibly readjust candidate
+ // entries. Also, it may be okay to remove this and instead aggregate from inner counts.
+ // Defaults to -1
+ private int mNumEntriesTotal = -1;
+ // The count of action entries from this provider, defaults to -1
+ private int mActionEntryCount = -1;
+ // The count of credential entries from this provider, defaults to -1
+ private int mCredentialEntryCount = -1;
+ // The *type-count* of the credential entries, defaults to -1
+ private int mCredentialEntryTypeCount = -1;
+ // The count of remote entries from this provider, defaults to -1
+ private int mRemoteEntryCount = -1;
+ // The count of authentication entries from this provider, defaults to -1
+ private int mAuthenticationEntryCount = -1;
+
+ public CandidatePhaseMetric() {
+ }
+
+ /* ---------- Latencies ---------- */
+
+ /* -- Timestamps -- */
+
+ public void setServiceBeganTimeNanoseconds(long serviceBeganTimeNanoseconds) {
+ this.mServiceBeganTimeNanoseconds = serviceBeganTimeNanoseconds;
+ }
+
+ public void setStartQueryTimeNanoseconds(long startQueryTimeNanoseconds) {
+ this.mStartQueryTimeNanoseconds = startQueryTimeNanoseconds;
+ }
+
+ public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
+ this.mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
+ }
+
+ public long getServiceBeganTimeNanoseconds() {
+ return this.mServiceBeganTimeNanoseconds;
+ }
+
+ public long getStartQueryTimeNanoseconds() {
+ return this.mStartQueryTimeNanoseconds;
+ }
+
+ public long getQueryFinishTimeNanoseconds() {
+ return this.mQueryFinishTimeNanoseconds;
+ }
+
+ /* -- Actual time delta latencies (for local utility) -- */
+
+ /**
+ * Returns the latency in microseconds for the query phase.
+ */
+ public int getQueryLatencyMicroseconds() {
+ return (int) ((this.getQueryFinishTimeNanoseconds()
+ - this.getStartQueryTimeNanoseconds()) / 1000);
+ }
+
+ /* --- Time Stamp Conversion to Microseconds from Reference --- */
+
+ /**
+ * We collect raw timestamps in nanoseconds for ease of collection. However, given the scope
+ * of our logging timeframe, and size considerations of the metric, we require these to give us
+ * the microsecond timestamps from the start reference point.
+ *
+ * @param specificTimestamp the timestamp to consider, must be greater than the reference
+ * @return the microsecond integer timestamp from service start to query began
+ */
+ public int getTimestampFromReferenceStartMicroseconds(long specificTimestamp) {
+ if (specificTimestamp < this.mServiceBeganTimeNanoseconds) {
+ Log.i(TAG, "The timestamp is before service started, falling back to default int");
+ return MetricUtilities.DEFAULT_INT_32;
+ }
+ return (int) ((specificTimestamp
+ - this.mServiceBeganTimeNanoseconds) / 1000);
+ }
+
+ /* ------------- Provider Query Status ------------ */
+
+ public void setProviderQueryStatus(int providerQueryStatus) {
+ this.mProviderQueryStatus = providerQueryStatus;
+ }
+
+ public int getProviderQueryStatus() {
+ return this.mProviderQueryStatus;
+ }
+
+ /* -------------- Candidate Uid ---------------- */
+
+ public void setCandidateUid(int candidateUid) {
+ this.mCandidateUid = candidateUid;
+ }
+
+ public int getCandidateUid() {
+ return this.mCandidateUid;
+ }
+
+ /* -------------- Session Id ---------------- */
+ public int getSessionId() {
+ return SESSION_ID;
+ }
+
+ /* -------------- Sequence Id ---------------- */
+
+ public void setSequenceId(int sequenceId) {
+ mSequenceId = sequenceId;
+ }
+
+ public int getSequenceId() {
+ return mSequenceId;
+ }
+
+ /* -------------- Query Returned Status ---------------- */
+
+ public void setQueryReturned(boolean queryReturned) {
+ mQueryReturned = queryReturned;
+ }
+
+ public boolean isQueryReturned() {
+ return mQueryReturned;
+ }
+
+ /* -------------- Has Exception Status ---------------- */
+
+ public void setHasException(boolean hasException) {
+ mHasException = hasException;
+ }
+
+ public boolean isHasException() {
+ return mHasException;
+ }
+
+ /* -------------- Number of Entries ---------------- */
+
+ public void setNumEntriesTotal(int numEntriesTotal) {
+ mNumEntriesTotal = numEntriesTotal;
+ }
+
+ public int getNumEntriesTotal() {
+ return mNumEntriesTotal;
+ }
+
+ /* -------------- Count of Action Entries ---------------- */
+
+ public void setActionEntryCount(int actionEntryCount) {
+ mActionEntryCount = actionEntryCount;
+ }
+
+ public int getActionEntryCount() {
+ return mActionEntryCount;
+ }
+
+ /* -------------- Count of Credential Entries ---------------- */
+
+ public void setCredentialEntryCount(int credentialEntryCount) {
+ mCredentialEntryCount = credentialEntryCount;
+ }
+
+ public int getCredentialEntryCount() {
+ return mCredentialEntryCount;
+ }
+
+ /* -------------- Count of Credential Entry Types ---------------- */
+
+ public void setCredentialEntryTypeCount(int credentialEntryTypeCount) {
+ mCredentialEntryTypeCount = credentialEntryTypeCount;
+ }
+
+ public int getCredentialEntryTypeCount() {
+ return mCredentialEntryTypeCount;
+ }
+
+ /* -------------- Count of Remote Entries ---------------- */
+
+ public void setRemoteEntryCount(int remoteEntryCount) {
+ mRemoteEntryCount = remoteEntryCount;
+ }
+
+ public int getRemoteEntryCount() {
+ return mRemoteEntryCount;
+ }
+
+ /* -------------- Count of Authentication Entries ---------------- */
+
+ public void setAuthenticationEntryCount(int authenticationEntryCount) {
+ mAuthenticationEntryCount = authenticationEntryCount;
+ }
+
+ public int getAuthenticationEntryCount() {
+ return mAuthenticationEntryCount;
+ }
+}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
deleted file mode 100644
index 9f438ec..0000000
--- a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.credentials.metrics;
-
-/**
- * The central candidate provider metric object that mimics our defined metric setup.
- * Some types are redundant across these metric collectors, but that has debug use-cases as
- * these data-types are available at different moments of the flow (and typically, one can feed
- * into the next).
- * TODO(b/270403549) - iterate on this in V3+
- */
-public class CandidateProviderMetric {
-
- private static final String TAG = "CandidateProviderMetric";
- private int mCandidateUid = -1;
-
- // Raw timestamp in nanoseconds, will be converted to microseconds for logging
-
- private long mStartQueryTimeNanoseconds = -1;
- private long mQueryFinishTimeNanoseconds = -1;
-
- private int mProviderQueryStatus = -1;
-
- public CandidateProviderMetric() {
- }
-
- /* ---------- Latencies ---------- */
-
- public void setStartQueryTimeNanoseconds(long startQueryTimeNanoseconds) {
- this.mStartQueryTimeNanoseconds = startQueryTimeNanoseconds;
- }
-
- public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
- this.mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
- }
-
- public long getStartQueryTimeNanoseconds() {
- return this.mStartQueryTimeNanoseconds;
- }
-
- public long getQueryFinishTimeNanoseconds() {
- return this.mQueryFinishTimeNanoseconds;
- }
-
- /**
- * Returns the latency in microseconds for the query phase.
- */
- public int getQueryLatencyMicroseconds() {
- return (int) ((this.getQueryFinishTimeNanoseconds()
- - this.getStartQueryTimeNanoseconds()) / 1000);
- }
-
- // TODO (in direct next dependent CL, so this is transient) - add reference timestamp in micro
- // seconds for this too.
-
- /* ------------- Provider Query Status ------------ */
-
- public void setProviderQueryStatus(int providerQueryStatus) {
- this.mProviderQueryStatus = providerQueryStatus;
- }
-
- public int getProviderQueryStatus() {
- return this.mProviderQueryStatus;
- }
-
- /* -------------- Candidate Uid ---------------- */
-
- public void setCandidateUid(int candidateUid) {
- this.mCandidateUid = candidateUid;
- }
-
- public int getCandidateUid() {
- return this.mCandidateUid;
- }
-}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
index 0310255..1a61091 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
@@ -64,7 +64,7 @@
/* ---------------- Latencies ------------------ */
- /* ----- Direct Latencies ------- */
+ /* ----- Direct Delta Latencies for Local Utility ------- */
/**
* In order for a chosen provider to be selected, the call must have successfully begun.
@@ -85,7 +85,7 @@
* metric.
*
* @param queryPhaseLatencyMicroseconds the millisecond latency for the query phase, typically
- * passed in through the {@link CandidateProviderMetric}
+ * passed in through the {@link CandidatePhaseMetric}
*/
public void setQueryPhaseLatencyMicroseconds(int queryPhaseLatencyMicroseconds) {
mQueryPhaseLatencyMicroseconds = queryPhaseLatencyMicroseconds;
@@ -106,7 +106,7 @@
/**
* Returns the full provider (invocation to response) latency in microseconds. Expects the
- * start time to be provided, such as from {@link CandidateProviderMetric}.
+ * start time to be provided, such as from {@link CandidatePhaseMetric}.
*/
public int getEntireProviderLatencyMicroseconds() {
return (int) ((this.mFinalFinishTimeNanoseconds
@@ -172,7 +172,7 @@
return mFinalFinishTimeNanoseconds;
}
- /* --- Time Stamp Conversion to Microseconds --- */
+ /* --- Time Stamp Conversion to Microseconds from Reference Point --- */
/**
* We collect raw timestamps in nanoseconds for ease of collection. However, given the scope
diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
index 5f062b0..31c6f6f 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
@@ -25,6 +25,8 @@
*/
public class InitialPhaseMetric {
private static final String TAG = "PreCandidateMetric";
+ // A sequence id to order united emits, due to split, this will statically always be 1
+ public static final int SEQUENCE_ID = 1;
// The api being called, default set to unknown
private int mApiName = ApiName.UNKNOWN.getMetricCode();
@@ -32,8 +34,6 @@
private int mCallerUid = -1;
// The session id to unite multiple atom emits, default to -1
private long mSessionId = -1;
- // A sequence id to order united emits, default to -1
- private int mSequenceId = -1;
private int mCountRequestClassType = -1;
// Raw timestamps in nanoseconds, *the only* one logged as such (i.e. 64 bits) since it is a
@@ -50,7 +50,7 @@
/* ---------- Latencies ---------- */
- /* -- Direct Latencies -- */
+ /* -- Direct Latency Utility -- */
public int getServiceStartToQueryLatencyMicroseconds() {
return (int) ((this.mCredentialServiceStartedTimeNanoseconds
@@ -108,15 +108,6 @@
return mSessionId;
}
- /* ------ SequenceId ------ */
-
- public void setSequenceId(int sequenceId) {
- mSequenceId = sequenceId;
- }
-
- public int getSequenceId() {
- return mSequenceId;
- }
/* ------ Count Request Class Types ------ */
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a4e563b..a220ad1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11219,7 +11219,7 @@
} else {
Objects.requireNonNull(admin, "ComponentName is null");
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
- || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
}
if (parent) {
userId = getProfileParentId(mInjector.userHandleGetCallingUserId());
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index 9501b96..9829e57 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -65,6 +65,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -121,6 +122,14 @@
protected InputMethodManagerService mInputMethodManagerService;
protected ServiceThread mServiceThread;
+ @BeforeClass
+ public static void setupClass() {
+ // Make sure DeviceConfig's lazy-initialized ContentProvider gets
+ // a real instance before we stub out all system services below.
+ // TODO(b/272229177): remove dependency on real ContentProvider
+ new InputMethodDeviceConfigs().destroy();
+ }
+
@Before
public void setUp() throws RemoteException {
mMockingSession =
@@ -231,6 +240,10 @@
@After
public void tearDown() {
+ if (mInputMethodManagerService != null) {
+ mInputMethodManagerService.mInputMethodDeviceConfigs.destroy();
+ }
+
if (mServiceThread != null) {
mServiceThread.quitSafely();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 0b4c70c2..485ce33 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -1968,36 +1968,6 @@
@SuppressWarnings("GuardedBy")
@Test
- public void testUpdateOomAdj_DoOne_PendingFinishAttach() {
- ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
- MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
- app.setPendingFinishAttach(true);
- app.mState.setHasForegroundActivities(false);
-
- sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
- updateOomAdj(app);
-
- assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ,
- SCHED_GROUP_DEFAULT);
- }
-
- @SuppressWarnings("GuardedBy")
- @Test
- public void testUpdateOomAdj_DoOne_TopApp_PendingFinishAttach() {
- ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
- MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
- app.setPendingFinishAttach(true);
- app.mState.setHasForegroundActivities(true);
-
- sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
- updateOomAdj(app);
-
- assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ,
- SCHED_GROUP_TOP_APP);
- }
-
- @SuppressWarnings("GuardedBy")
- @Test
public void testUpdateOomAdj_UidIdle_StopService() {
final ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
index 2774803..239b6fd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
@@ -139,6 +139,13 @@
}
@Test
+ public void testInvalidMode() {
+ assertThrows(IllegalArgumentException.class, () -> new UserVisibilityMediator(
+ /* visibleBackgroundUsersOnDisplaysEnabled= */ false,
+ /* visibleBackgroundUserOnDefaultDisplayAllowed= */ true, mHandler));
+ }
+
+ @Test
public final void testAssignUserToDisplayOnStart_invalidUserIds() {
assertThrows(IllegalArgumentException.class, () -> mMediator
.assignUserToDisplayOnStart(USER_NULL, USER_ID, FG, DEFAULT_DISPLAY));
diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
index e68a8a0..01563e2 100644
--- a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
@@ -106,6 +106,64 @@
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
}
+ @Test
+ public void testStrictRepeatedLimiting() throws Exception {
+ // The first 6 entries should not be rate limited.
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ // The 7th entry of the same process should be rate limited.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // After 11 minutes there should be nothing left in the buffer and the same type of entry
+ // should not get rate limited anymore.
+ mClock.setOffsetMillis(11 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ // The first 6 entries should not be rate limited again.
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // The 7th entry of the same process should be rate limited.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // After 11 more minutes there should be nothing left in the buffer and the same type of
+ // entry should not get rate limited anymore.
+ mClock.setOffsetMillis(22 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // Repeated crashes after the last reset being rate limited should be restricted faster.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // We now need to wait 61 minutes for the buffer should be empty again.
+ mClock.setOffsetMillis(83 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // After yet another 61 minutes, this time without triggering rate limiting, the strict
+ // limiting should be turnd off.
+ mClock.setOffsetMillis(144 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // As rate limiting was not triggered in the last reset, after another 11 minutes the
+ // buffer should still act as normal.
+ mClock.setOffsetMillis(155 * 60 * 1000);
+ // The first 6 entries should not be rate limited.
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ // The 7th entry of the same process should be rate limited.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ }
+
private static class TestClock implements DropboxRateLimiter.Clock {
long mOffsetMillis = 0L;
diff --git a/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java b/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
index b7085f15..169210f 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
@@ -52,8 +52,10 @@
private static final String CALLING_PACKAGE_NAME_2 = "com.credman.app2";
private static final String MDOC_CREDENTIAL_TYPE = "MDOC";
private static final String PASSKEY_CREDENTIAL_TYPE = "PASSKEY";
- private static final String FLATTENED_REQUEST = "FLATTENED_REQ";
- private static final String FLATTENED_REQUEST_2 = "FLATTENED_REQ_2";
+ private static final String FLATTENED_REGISTRY =
+ "FLATTENED_REQ;FLATTENED_REQ123;FLATTENED_REQa";
+ private static final String FLATTENED_REGISTRY_2 = "FLATTENED_REQ_2";
+ private static final String FLATTENED_REQUEST = "FLATTENED_REQ;FLATTENED_REQ123";
private CredentialDescriptionRegistry mCredentialDescriptionRegistry;
private CredentialEntry mEntry;
@@ -104,12 +106,12 @@
@Test
public void testEvictProvider_existingProviders_succeeds() {
final CredentialDescription credentialDescription =
- new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+ new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY,
Collections.emptyList());
final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
new RegisterCredentialDescriptionRequest(credentialDescription);
final CredentialDescription credentialDescription2 =
- new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST_2,
+ new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY_2,
Collections.emptyList());
final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest2 =
new RegisterCredentialDescriptionRequest(credentialDescription2);
@@ -130,12 +132,12 @@
@Test
public void testGetMatchingProviders_existingProviders_succeeds() {
final CredentialDescription credentialDescription =
- new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+ new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY,
Collections.emptyList());
final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
new RegisterCredentialDescriptionRequest(credentialDescription);
final CredentialDescription credentialDescription2 =
- new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+ new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY,
Collections.emptyList());
final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest2 =
new RegisterCredentialDescriptionRequest(credentialDescription2);
@@ -171,11 +173,11 @@
public void testExecuteRegisterRequest_existingProviders_filterSucceeds() {
final CredentialDescription credentialDescription =
new CredentialDescription(MDOC_CREDENTIAL_TYPE,
- FLATTENED_REQUEST,
+ FLATTENED_REGISTRY,
List.of(mEntry, mEntry2));
final CredentialDescription credentialDescription2 =
new CredentialDescription(PASSKEY_CREDENTIAL_TYPE,
- FLATTENED_REQUEST_2,
+ FLATTENED_REGISTRY_2,
List.of(mEntry3));
final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
new RegisterCredentialDescriptionRequest(Set.of(credentialDescription,
diff --git a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
index 3d52ac5..4c8e70a 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
@@ -36,7 +36,6 @@
import android.content.pm.SigningInfo;
import android.credentials.Credential;
import android.credentials.CredentialOption;
-import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialResponse;
import android.credentials.ui.GetCredentialProviderData;
@@ -45,7 +44,6 @@
import android.os.Bundle;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.CredentialEntry;
-import android.service.credentials.CredentialProviderInfoFactory;
import android.service.credentials.CredentialProviderService;
import android.service.credentials.GetCredentialRequest;
@@ -61,7 +59,6 @@
import org.mockito.MockitoAnnotations;
import java.security.cert.CertificateException;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -89,7 +86,6 @@
@Mock private GetRequestSession mGetRequestSession;
private CredentialOption mGetCredentialOption;
@Mock private ServiceInfo mServiceInfo;
- private CredentialProviderInfo mCredentialProviderInfo;
private CallingAppInfo mCallingAppInfo;
@Mock private CredentialDescriptionRegistry mCredentialDescriptionRegistry;
private Bundle mRetrievalData;
@@ -111,12 +107,6 @@
mGetCredentialOption = new CredentialOption(CREDENTIAL_TYPE, mRetrievalData,
new Bundle(), false);
when(mServiceInfo.getComponentName()).thenReturn(CREDENTIAL_PROVIDER_COMPONENT);
- mCredentialProviderInfo = CredentialProviderInfoFactory
- .createForTests(mServiceInfo,
- /* overrideLabel= */ "test",
- /* isSystemProvider= */ false,
- /* isEnabled= */ true,
- /* capabilities= */ Collections.EMPTY_LIST);
CredentialDescriptionRegistry.setSession(USER_ID_1, mCredentialDescriptionRegistry);
mResponse = new HashSet<>();
mSlice = createSlice();
@@ -130,7 +120,7 @@
when(mCredentialDescriptionRegistry.getFilteredResultForProvider(anyString(), anyString()))
.thenReturn(mResponse);
mProviderRegistryGetSession = ProviderRegistryGetSession
- .createNewSession(context, USER_ID_1, mGetRequestSession, mCredentialProviderInfo,
+ .createNewSession(context, USER_ID_1, mGetRequestSession,
mCallingAppInfo,
CALLING_PACKAGE_NAME,
mGetCredentialOption);
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
index dd0cd96..fd1889c 100644
--- a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import android.hardware.display.DisplayManager;
@@ -49,6 +50,7 @@
private static final float FLOAT_TOLERANCE = 0.01f;
private static final int DISPLAY_ID = 1;
private static final int DISPLAY_ID_OTHER = 2;
+ private static final int DISPLAY_ID_ADDED = 3;
SkinThermalStatusObserver mObserver;
@@ -167,6 +169,42 @@
assertEquals(120, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
}
+ @Test
+ public void testDisplayAdded() {
+ // GIVEN 2 displays with no thermalThrottling config AND temperature level CRITICAL
+ mObserver.observe();
+ mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_CRITICAL));
+ // WHEN new display is added
+ mObserver.onDisplayAdded(DISPLAY_ID_ADDED);
+ mHandler.flush();
+ // THEN 3rd vote is added to storage with (0,60) render refresh rate(default behaviour)
+ assertEquals(3, mStorage.mVoteRegistry.size());
+
+ SparseArray<DisplayModeDirector.Vote> displayVotes = mStorage.mVoteRegistry.get(
+ DISPLAY_ID_ADDED);
+ assertEquals(1, displayVotes.size());
+
+ DisplayModeDirector.Vote vote = displayVotes.get(
+ DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE);
+ assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
+ assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testDisplayAddedAndThenImmediatelyRemoved() {
+ // GIVEN 2 displays with no thermalThrottling config AND temperature level CRITICAL
+ mObserver.observe();
+ mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_CRITICAL));
+ // WHEN new display is added and immediately removed
+ mObserver.onDisplayAdded(DISPLAY_ID_ADDED);
+ mObserver.onDisplayRemoved(DISPLAY_ID_ADDED);
+ mHandler.flush();
+ // THEN there are 2 votes in registry
+ assertEquals(2, mStorage.mVoteRegistry.size());
+ assertNotNull(mStorage.mVoteRegistry.get(DISPLAY_ID));
+ assertNotNull(mStorage.mVoteRegistry.get(DISPLAY_ID_OTHER));
+ }
+
private static Temperature createTemperature(@Temperature.ThrottlingStatus int status) {
return new Temperature(40.0f, Temperature.TYPE_SKIN, "test_temp", status);
}
diff --git a/services/tests/servicestests/src/com/android/server/dreams/OWNERS b/services/tests/servicestests/src/com/android/server/dreams/OWNERS
new file mode 100644
index 0000000..2f19cf5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/dreams/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/dreams/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index c9612cd..ad63da5 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -39,6 +39,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.hardware.authsecret.IAuthSecret;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
@@ -97,6 +98,7 @@
MockLockSettingsContext mContext;
LockSettingsStorageTestable mStorage;
+ Resources mResources;
FakeGateKeeperService mGateKeeperService;
NotificationManager mNotificationManager;
UserManager mUserManager;
@@ -122,6 +124,7 @@
@Before
public void setUp_baseServices() throws Exception {
+ mResources = createMockResources();
mGateKeeperService = new FakeGateKeeperService();
mNotificationManager = mock(NotificationManager.class);
mUserManager = mock(UserManager.class);
@@ -146,7 +149,7 @@
LocalServices.addService(WindowManagerInternal.class, mMockWindowManager);
final Context origContext = InstrumentationRegistry.getContext();
- mContext = new MockLockSettingsContext(origContext,
+ mContext = new MockLockSettingsContext(origContext, mResources,
mSettingsRule.mockContentResolver(origContext), mUserManager, mNotificationManager,
mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class),
mock(KeyguardManager.class), mFingerprintManager, mFaceManager, mPackageManager);
@@ -245,6 +248,22 @@
mLocalService = LocalServices.getService(LockSettingsInternal.class);
}
+ private Resources createMockResources() {
+ Resources res = mock(Resources.class);
+
+ // Set up some default configs, copied from core/res/res/values/config.xml
+ when(res.getBoolean(eq(com.android.internal.R.bool.config_disableLockscreenByDefault)))
+ .thenReturn(false);
+ when(res.getBoolean(
+ eq(com.android.internal.R.bool.config_enableCredentialFactoryResetProtection)))
+ .thenReturn(true);
+ when(res.getBoolean(eq(com.android.internal.R.bool.config_isMainUserPermanentAdmin)))
+ .thenReturn(true);
+ when(res.getBoolean(eq(com.android.internal.R.bool.config_strongAuthRequiredOnBoot)))
+ .thenReturn(true);
+ return res;
+ }
+
protected void setDeviceProvisioned(boolean provisioned) {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, provisioned ? 1 : 0);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 05208441e..10ed882 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -34,6 +34,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
@@ -106,8 +107,8 @@
when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0));
MockLockSettingsContext context = new MockLockSettingsContext(origContext,
- mSettingsRule.mockContentResolver(origContext), mockUserManager,
- mock(NotificationManager.class), mock(DevicePolicyManager.class),
+ mock(Resources.class), mSettingsRule.mockContentResolver(origContext),
+ mockUserManager, mock(NotificationManager.class), mock(DevicePolicyManager.class),
mock(StorageManager.class), mock(TrustManager.class), mock(KeyguardManager.class),
mock(FingerprintManager.class), mock(FaceManager.class),
mock(PackageManager.class));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
index 21c367b..96d7cbe 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Handler;
@@ -36,6 +37,7 @@
public class MockLockSettingsContext extends ContextWrapper {
+ private final Resources mResources;
private final ContentResolver mContentResolver;
private final UserManager mUserManager;
private final NotificationManager mNotificationManager;
@@ -47,13 +49,14 @@
private final FaceManager mFaceManager;
private final PackageManager mPackageManager;
- public MockLockSettingsContext(Context base, ContentResolver contentResolver,
- UserManager userManager, NotificationManager notificationManager,
- DevicePolicyManager devicePolicyManager, StorageManager storageManager,
- TrustManager trustManager, KeyguardManager keyguardManager,
- FingerprintManager fingerprintManager, FaceManager faceManager,
- PackageManager packageManager) {
+ public MockLockSettingsContext(Context base, Resources resources,
+ ContentResolver contentResolver, UserManager userManager,
+ NotificationManager notificationManager, DevicePolicyManager devicePolicyManager,
+ StorageManager storageManager, TrustManager trustManager,
+ KeyguardManager keyguardManager, FingerprintManager fingerprintManager,
+ FaceManager faceManager, PackageManager packageManager) {
super(base);
+ mResources = resources;
mContentResolver = contentResolver;
mUserManager = userManager;
mNotificationManager = notificationManager;
@@ -67,6 +70,11 @@
}
@Override
+ public Resources getResources() {
+ return mResources;
+ }
+
+ @Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 2affe92..8b178dd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -41,7 +41,7 @@
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
@@ -1326,11 +1326,10 @@
when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
LockPatternUtils.CREDENTIAL_TYPE_PIN);
- StartLockscreenValidationRequest request =
+ RemoteLockscreenValidationSession request =
mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
- int credetialsType = request.getLockscreenUiType();
- assertThat(credetialsType).isEqualTo(KeyguardManager.PIN);
+ assertThat(request.getLockType()).isEqualTo(KeyguardManager.PIN);
assertThat(request.getRemainingAttempts()).isEqualTo(5);
verify(mLockSettingsService).getCredentialType(anyInt());
}
@@ -1340,11 +1339,10 @@
LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 3);
- StartLockscreenValidationRequest request =
+ RemoteLockscreenValidationSession request =
mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
- int credetialsType = request.getLockscreenUiType();
- assertThat(credetialsType).isEqualTo(KeyguardManager.PATTERN);
+ assertThat(request.getLockType()).isEqualTo(KeyguardManager.PATTERN);
assertThat(request.getRemainingAttempts()).isEqualTo(2);
}
@Test
@@ -1353,24 +1351,23 @@
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 7);
- StartLockscreenValidationRequest request =
+ RemoteLockscreenValidationSession request =
mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
- int credetialsType = request.getLockscreenUiType();
assertThat(request.getRemainingAttempts()).isEqualTo(0);
- assertThat(credetialsType).isEqualTo(KeyguardManager.PASSWORD);
+ assertThat(request.getLockType()).isEqualTo(KeyguardManager.PASSWORD);
}
@Test
public void validateRemoteLockscreen_noActiveSession() throws Exception {
when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
- LockPatternUtils.CREDENTIAL_TYPE_NONE);
- try {
- mRecoverableKeyStoreManager.validateRemoteLockscreen(INVALID_GUESS,
- mLockSettingsService);
- fail("should have thrown");
- } catch (IllegalStateException e) {
- assertThat(e.getMessage()).contains("session");
- }
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
+
+ RemoteLockscreenValidationResult result =
+ mRecoverableKeyStoreManager.validateRemoteLockscreen(INVALID_GUESS,
+ mLockSettingsService);
+
+ assertThat(result.getResultCode()).isEqualTo(
+ RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED);
}
@Test
public void validateRemoteLockscreen_decryptionError() throws Exception {
@@ -1456,7 +1453,7 @@
}
private byte[] encryptCredentialsForNewSession(byte[] credentials) throws Exception {
- StartLockscreenValidationRequest request =
+ RemoteLockscreenValidationSession request =
mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
PublicKey publicKey = SecureBox.decodePublicKey(request.getSourcePublicKey());
return SecureBox.encrypt(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 354420f..41a9504 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -416,7 +416,7 @@
// Shell permisssions will override permissions of our app, so add all necessary permissions
// for this test here:
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
- "android.permission.WRITE_DEVICE_CONFIG",
+ "android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG",
"android.permission.READ_DEVICE_CONFIG",
"android.permission.READ_CONTACTS");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 49f215a..6147633 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2377,7 +2377,7 @@
.setScreenOrientation(SCREEN_ORIENTATION_BEHIND)
.build();
final int topOrientation = activityTop.getRequestedConfigurationOrientation();
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, topOrientation);
+ assertEquals(ORIENTATION_PORTRAIT, topOrientation);
}
private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index e3cb5fb..b46a3b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1728,7 +1728,7 @@
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
- 0 /* launchMode */);
+ 0 /* launchMode */, null /* componentName */);
info.requiredDisplayCategory = "automotive";
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
.build();
@@ -1754,7 +1754,7 @@
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
- 0 /* launchMode */);
+ 0 /* launchMode */, null /* componentName */);
info.requiredDisplayCategory = "automotive";
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
.build();
@@ -1780,7 +1780,7 @@
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
- 0 /* launchMode */);
+ 0 /* launchMode */, null /* componentName */);
info.requiredDisplayCategory = "automotive";
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
.build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index 7830e90..3a456fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -16,8 +16,9 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
@@ -79,24 +80,24 @@
}
@Test
- public void testReturnsSkipIfTaskNotInFreeform() {
- final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
- WINDOWING_MODE_FULLSCREEN).build();
+ public void testReturnsSkipIfTaskNotUsingActivityTypeStandard() {
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_ASSISTANT).build();
assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
}
@Test
public void testReturnsSkipIfCurrentParamsHasBounds() {
- final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
- WINDOWING_MODE_FREEFORM).build();
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).build();
mCurrent.mBounds.set(/* left */ 0, /* top */ 0, /* right */ 100, /* bottom */ 100);
assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
}
@Test
public void testUsesDefaultBounds() {
- final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
- WINDOWING_MODE_FREEFORM).build();
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).build();
assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
assertEquals(dpiToPx(task, 840), mResult.mBounds.width());
assertEquals(dpiToPx(task, 630), mResult.mBounds.height());
@@ -104,8 +105,8 @@
@Test
public void testUsesDisplayAreaAndWindowingModeFromSource() {
- final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
- WINDOWING_MODE_FREEFORM).build();
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).build();
TaskDisplayArea mockTaskDisplayArea = mock(TaskDisplayArea.class);
mCurrent.mPreferredTaskDisplayArea = mockTaskDisplayArea;
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 74dd361..12f9a9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -448,12 +448,15 @@
final String taskAffinity = "affinity";
final int uid = 10123;
final Task task1 = createTaskBuilder(".Task1").build();
- task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE);
+ final ComponentName componentName = getUniqueComponentName();
+ task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE,
+ componentName);
mRecentTasks.add(task1);
// Add another task to recents, and make sure the previous task was removed.
final Task task2 = createTaskBuilder(".Task2").build();
- task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE);
+ task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE,
+ componentName);
mRecentTasks.add(task2);
assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
@@ -461,7 +464,7 @@
// Add another single-instance task to recents, and make sure no task is removed.
final Task task3 = createTaskBuilder(".Task3").build();
task3.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid,
- LAUNCH_SINGLE_INSTANCE);
+ LAUNCH_SINGLE_INSTANCE, componentName);
mRecentTasks.add(task3);
assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 65c7125..753cc62 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -384,7 +384,25 @@
}
@Test
- public void testTranslucentActivitiesDontGoInSizeCompactMode() {
+ public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() {
+ mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+ setUpDisplaySizeWithApp(2000, 1000);
+ mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ // Mock the activity as embedded without additional TaskFragment layer in the task for
+ // simplicity.
+ doReturn(true).when(mActivity).isEmbedded();
+ // Translucent Activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).build();
+ doReturn(false).when(translucentActivity).matchParentBounds();
+ doReturn(false).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+ // Check the strategy has not being applied
+ assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
+ }
+
+ @Test
+ public void testTranslucentActivitiesDontGoInSizeCompatMode() {
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
setUpDisplaySizeWithApp(2800, 1400);
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2472,11 +2490,11 @@
assertFalse(mActivity.inSizeCompatMode());
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
-
- assertTrue(mActivity.inSizeCompatMode());
- // We should remember the original orientation.
+ // Activity is not in size compat mode because the orientation change request came from the
+ // app itself
+ assertFalse(mActivity.inSizeCompatMode());
assertEquals(mActivity.getResolvedOverrideConfiguration().orientation,
- Configuration.ORIENTATION_PORTRAIT);
+ Configuration.ORIENTATION_UNDEFINED);
}
@Test
@@ -3051,6 +3069,25 @@
}
@Test
+ public void testAppRequestsOrientationChange_notInSizeCompat() {
+ setUpDisplaySizeWithApp(2200, 1800);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity is not in size compat mode because the orientation change request came from the
+ // app itself
+ assertFalse(mActivity.inSizeCompatMode());
+
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
+ // Activity should go into size compat mode now because the orientation change came from the
+ // system (device rotation)
+ assertTrue(mActivity.inSizeCompatMode());
+ }
+
+ @Test
public void testLetterboxDetailsForStatusBar_noLetterbox() {
setUpDisplaySizeWithApp(2800, 1000);
addStatusBar(mActivity.mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
index c538727..e30206e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
@@ -41,6 +41,7 @@
@Presubmit
public class SurfaceSyncGroupTest {
private static final String TAG = "SurfaceSyncGroupTest";
+ private static final int TIMEOUT_MS = 100;
private final Executor mExecutor = Runnable::run;
@@ -86,7 +87,7 @@
syncTarget2.markSyncReady();
- finishedLatch.await(5, TimeUnit.SECONDS);
+ finishedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch.getCount());
}
@@ -124,13 +125,13 @@
syncTarget1.markSyncReady();
- finishedLatch1.await(5, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch1.getCount());
assertNotEquals(0, finishedLatch2.getCount());
syncTarget2.markSyncReady();
- finishedLatch2.await(5, TimeUnit.SECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch2.getCount());
}
@@ -156,17 +157,17 @@
// Finish syncTarget2 first to test that the syncGroup is not complete until the merged sync
// is also done.
syncTarget2.markSyncReady();
- finishedLatch2.await(1, TimeUnit.SECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
// Sync did not complete yet
assertNotEquals(0, finishedLatch2.getCount());
syncTarget1.markSyncReady();
// The first sync will still get a callback when it's sync requirements are done.
- finishedLatch1.await(5, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch1.getCount());
- finishedLatch2.await(5, TimeUnit.SECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch2.getCount());
}
@@ -189,7 +190,7 @@
syncTarget1.markSyncReady();
// The first sync will still get a callback when it's sync requirements are done.
- finishedLatch1.await(5, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch1.getCount());
syncGroup2.add(syncGroup1, null /* runnable */);
@@ -198,7 +199,7 @@
// Verify that the second sync will receive complete since the merged sync was already
// completed before the merge.
- finishedLatch2.await(5, TimeUnit.SECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch2.getCount());
}
@@ -232,8 +233,8 @@
syncTarget3.markSyncReady();
// Neither SyncGroup will be ready.
- finishedLatch1.await(1, TimeUnit.SECONDS);
- finishedLatch2.await(1, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(1, finishedLatch1.getCount());
assertEquals(1, finishedLatch2.getCount());
@@ -241,8 +242,8 @@
syncTarget2.markSyncReady();
// Both sync groups should be ready after target2 completed.
- finishedLatch1.await(5, TimeUnit.SECONDS);
- finishedLatch2.await(5, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch1.getCount());
assertEquals(0, finishedLatch2.getCount());
}
@@ -275,8 +276,8 @@
syncTarget1.markSyncReady();
// Only SyncGroup1 will be ready, but SyncGroup2 still needs its own targets to be ready.
- finishedLatch1.await(1, TimeUnit.SECONDS);
- finishedLatch2.await(1, TimeUnit.SECONDS);
+ finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch1.getCount());
assertEquals(1, finishedLatch2.getCount());
@@ -284,7 +285,7 @@
syncTarget3.markSyncReady();
// SyncGroup2 is finished after target3 completed.
- finishedLatch2.await(1, TimeUnit.SECONDS);
+ finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch2.getCount());
}
@@ -357,6 +358,27 @@
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
+
+ assertEquals(0, finishedLatch.getCount());
+ }
+
+ public void testSurfaceSyncGroupTimeout() throws InterruptedException {
+ final CountDownLatch finishedLatch = new CountDownLatch(1);
+ SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG);
+ syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown);
+ SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1");
+ SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2");
+
+ syncGroup.add(syncTarget1, null /* runnable */);
+ syncGroup.add(syncTarget2, null /* runnable */);
+ syncGroup.markSyncReady();
+
+ syncTarget1.markSyncReady();
+ assertNotEquals(0, finishedLatch.getCount());
+
+ // Never finish syncTarget2 so it forces the timeout. Timeout is 1 second so wait a little
+ // over 1 second to make sure it completes.
+ finishedLatch.await(1100, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch.getCount());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index ef2b691..65631ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -52,6 +52,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.DisplayShape;
@@ -62,6 +63,7 @@
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import android.window.ClientWindowFrames;
import androidx.test.filters.SmallTest;
@@ -340,6 +342,29 @@
}
@Test
+ public void testWallpaperReportConfigChange() {
+ final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent);
+ createWallpaperTargetWindow(mDisplayContent);
+ final WallpaperWindowToken wallpaperToken = wallpaperWindow.mToken.asWallpaperToken();
+ makeWindowVisible(wallpaperWindow);
+ wallpaperWindow.mLayoutSeq = mDisplayContent.mLayoutSeq;
+ // Assume the token was invisible and the latest config was reported.
+ wallpaperToken.commitVisibility(false);
+ wallpaperWindow.fillClientWindowFramesAndConfiguration(new ClientWindowFrames(),
+ new MergedConfiguration(), true /* useLatestConfig */, false /* relayoutVisible */);
+ assertTrue(wallpaperWindow.isLastConfigReportedToClient());
+
+ final Rect bounds = wallpaperToken.getBounds();
+ wallpaperToken.setBounds(new Rect(0, 0, bounds.width() / 2, bounds.height() / 2));
+ assertFalse(wallpaperWindow.isLastConfigReportedToClient());
+ // If there is a pending config change when changing to visible, it should tell the client
+ // to redraw by WindowState#reportResized.
+ wallpaperToken.commitVisibility(true);
+ waitUntilHandlersIdle();
+ assertTrue(wallpaperWindow.isLastConfigReportedToClient());
+ }
+
+ @Test
public void testWallpaperTokenVisibility() {
final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
final WindowState wallpaperWindow = createWallpaperWindow(dc);
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index cbac34f..0325ba6 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -31,6 +31,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
@@ -258,6 +259,9 @@
*/
public static void showErrorIfSubscriptionAssociatedWithManagedProfile(Context context,
int subId) {
+ if (!isSwitchToManagedProfileDialogFlagEnabled()) {
+ return;
+ }
final long token = Binder.clearCallingIdentity();
try {
SubscriptionManager subscriptionManager = context.getSystemService(
@@ -286,6 +290,11 @@
}
}
+ public static boolean isSwitchToManagedProfileDialogFlagEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
+ "enable_switch_to_managed_profile_dialog", false);
+ }
+
/**
* Check if the process with given uid is foreground.
*
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c4d760f..dd7e2d7 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -997,7 +997,8 @@
*/
public static final int IWLAN_CONGESTION = 0x3C8C;
- /** IKE configuration error resulting in failure */
+ // Below IWLAN error codes are defined by the UE and do not relate to any 3GPP spec value
+ /** IKE configuration error resulting in failure */
public static final int IWLAN_IKEV2_CONFIG_FAILURE = 0x4000;
/**
* Sent in the response to an IKE_AUTH message when, for some reason,
@@ -1014,6 +1015,57 @@
public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 0x4005;
/** Expected to update or bring down an ePDG tunnel, but no tunnel found*/
public static final int IWLAN_TUNNEL_NOT_FOUND = 0x4006;
+ /**
+ * Failed to apply tunnel transform
+ *
+ * @hide
+ */
+ public static final int IWLAN_TUNNEL_TRANSFORM_FAILED = 0x4007;
+ /**
+ * IWLAN PDN setup failed due to Wi-Fi lost during IKE tunnel setup,
+ * match exception reported by IKE module
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_NETWORK_LOST_EXCEPTION = 0x4008;
+ /**
+ * Carrier-specific error codes during IKEv2 SA setup
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_PRIVATE_PROTOCOL_ERROR = 0x4009;
+ /**
+ * IKE Session closed before child session opened
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED = 0x400A;
+ /**
+ * IKE Init timeout, no response from EPDG
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_INIT_TIMEOUT = 0x400B;
+ /**
+ * DPD message does not get an ack after the re-tx attempts and duration, i.e., times out.
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_DPD_TIMEOUT = 0x400C;
+ /**
+ * The Wi-Fi to Wi-Fi handover of the IMS PDN fails because the network does not respond to the
+ * MOBIKE/rekey mobility message in the expected manner
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_MOBILITY_TIMEOUT = 0x400D;
+ /**
+ * IKE client sent "IKE AUTH request 3" to the network but got "Internal address failure" from
+ * the network since no internal addresses can be assigned.
+ *
+ * @hide
+ */
+ public static final int IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE = 0x400E;
// OEM sepecific error codes. To be used by OEMs when they don't
// want to reveal error code which would be replaced by ERROR_UNSPECIFIED
@@ -1508,6 +1560,16 @@
sFailCauseMap.put(IWLAN_DNS_RESOLUTION_NAME_FAILURE, "IWLAN_DNS_RESOLUTION_NAME_FAILURE");
sFailCauseMap.put(IWLAN_DNS_RESOLUTION_TIMEOUT, "IWLAN_DNS_RESOLUTION_TIMEOUT");
sFailCauseMap.put(IWLAN_TUNNEL_NOT_FOUND, "IWLAN_TUNNEL_NOT_FOUND");
+ sFailCauseMap.put(IWLAN_TUNNEL_TRANSFORM_FAILED, "IWLAN_TUNNEL_TRANSFORM_FAILED");
+ sFailCauseMap.put(IWLAN_IKE_INIT_TIMEOUT, "IWLAN_IKE_INIT_TIMEOUT");
+ sFailCauseMap.put(IWLAN_IKE_NETWORK_LOST_EXCEPTION, "IWLAN_IKE_NETWORK_LOST_EXCEPTION");
+ sFailCauseMap.put(IWLAN_IKE_PRIVATE_PROTOCOL_ERROR, "IWLAN_IKE_PRIVATE_PROTOCOL_ERROR");
+ sFailCauseMap.put(IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED,
+ "IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED");
+ sFailCauseMap.put(IWLAN_IKE_DPD_TIMEOUT, "IWLAN_IKE_DPD_TIMEOUT");
+ sFailCauseMap.put(IWLAN_IKE_MOBILITY_TIMEOUT, "IWLAN_IKE_MOBILITY_TIMEOUT");
+ sFailCauseMap.put(IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE,
+ "IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE");
sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 788d0e8..cf0561d 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1530,6 +1530,10 @@
* @return the E.164 representation, or null if the given phone number is not valid.
*/
public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
+ if (defaultCountryIso != null) {
+ defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+ }
+
return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.E164);
}
@@ -1541,6 +1545,10 @@
* @return the RFC3966 representation, or null if the given phone number is not valid.
*/
public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) {
+ if (defaultCountryIso != null) {
+ defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+ }
+
return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.RFC3966);
}
@@ -1591,6 +1599,10 @@
return false;
}
+ if (defaultCountryIso != null) {
+ defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+ }
+
PhoneNumberUtil util = PhoneNumberUtil.getInstance();
try {
PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
@@ -1619,6 +1631,10 @@
return phoneNumber;
}
+ if (defaultCountryIso != null) {
+ defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+ }
+
PhoneNumberUtil util = PhoneNumberUtil.getInstance();
String result = null;
try {
@@ -1671,6 +1687,10 @@
*/
public static String formatNumber(
String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
+ if (defaultCountryIso != null) {
+ defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+ }
+
int len = phoneNumber.length();
for (int i = 0; i < len; i++) {
if (!isDialable(phoneNumber.charAt(i))) {
@@ -2900,7 +2920,11 @@
PhoneNumberUtil util = PhoneNumberUtil.getInstance();
PhoneNumber n1;
PhoneNumber n2;
- defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+
+ if (defaultCountryIso != null) {
+ defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+ }
+
try {
n1 = util.parseAndKeepRawInput(number1, defaultCountryIso);
n2 = util.parseAndKeepRawInput(number2, defaultCountryIso);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 758372a..5cbbe37 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -4374,7 +4374,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
public void setSubscriptionUserHandle(int subscriptionId, @Nullable UserHandle userHandle) {
if (!isValidSubscriptionId(subscriptionId)) {
@@ -4409,7 +4408,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
public @Nullable UserHandle getSubscriptionUserHandle(int subscriptionId) {
if (!isValidSubscriptionId(subscriptionId)) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index eb537bb..323fa6f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1848,6 +1848,22 @@
String gid2, String plmn, String spn, String carrierPrivilegeRules, String apn);
/**
+ * Forcibly sets a package as a carrier provisioning package.
+ *
+ * This override is ephemeral, and will disappear upon phone process restart (including
+ * device reboot).
+ *
+ * WARNING: This API is meant for testing purposes only. This enables automated testing for
+ * cases where the carrier service package is used as a permissioning gate for features such as
+ * restricted carrier network access (restricted APNs and Merged Carrier Wifi)
+ *
+ * @param carrierServicePackage The package that should be marked as the carrier service
+ * package, or {@code null} to disable the override.
+ */
+ void setCarrierServicePackageOverride(int subId, String carrierServicePackage,
+ String callingPackage);
+
+ /**
* A test API to return installed carrier id list version.
*/
int getCarrierIdListVersion(int subId);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 0e852b6..6e4e937 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -58,7 +58,51 @@
}
/**
- * Expands the PIP window my using the pinch out gesture.
+ * Drags the PIP window away from the screen edge while not crossing the display center.
+ *
+ * @throws IllegalStateException if default display bounds are not available
+ * @return initial bounds of the PIP window
+ */
+ fun dragPipWindowAwayFromEdge(wmHelper: WindowManagerStateHelper, steps: Int): Rect {
+ val initWindowRect = getWindowRect(wmHelper).clone()
+
+ // initial pointer at the center of the window
+ val startX = initWindowRect.centerX()
+ val y = initWindowRect.centerY()
+
+ val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+ ?: throw IllegalStateException("Default display is null")
+
+ // the offset to the right of the display center to drag the window to
+ val offset = 20
+
+ // the actual final x coordinate with the offset included
+ // if the pip window is closer to the right edge of the display the offset is positive
+ // otherwise the offset is negative
+ val endX = displayRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) 1 else -1)
+
+ // drag the window to the left but not beyond the center of the display
+ uiDevice.drag(startX, y, endX, y, steps)
+
+ return initWindowRect
+ }
+
+ /**
+ * Returns true if PIP window is closer to the right edge of the display than left.
+ *
+ * @throws IllegalStateException if default display bounds are not available
+ */
+ private fun isCloserToRightEdge(wmHelper: WindowManagerStateHelper): Boolean {
+ val windowRect = getWindowRect(wmHelper)
+
+ val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+ ?: throw IllegalStateException("Default display is null")
+
+ return windowRect.centerX() > displayRect.centerX()
+ }
+
+ /**
+ * Expands the PIP window by using the pinch out gesture.
*
* @param percent The percentage by which to increase the pip window size.
* @throws IllegalArgumentException if percentage isn't between 0.0f and 1.0f
@@ -106,7 +150,7 @@
}
/**
- * Minimizes the PIP window my using the pinch in gesture.
+ * Minimizes the PIP window by using the pinch in gesture.
*
* @param percent The percentage by which to decrease the pip window size.
* @throws IllegalArgumentException if percentage isn't between 0.0f and 1.0f
@@ -307,6 +351,27 @@
.waitForAndVerify()
}
+ /**
+ * Waits until the PIP window snaps horizontally to the provided bounds.
+ *
+ * @param finalRightX the final x coordinate of the right edge of the pip window
+ */
+ fun waitForPipToSnapTo(wmHelper: WindowManagerStateHelper, finalBounds: android.graphics.Rect) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("pipWindowSnapped") {
+ val pipAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ this.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val pipRegionBounds = pipAppWindow.frameRegion.bounds
+ return@add pipRegionBounds.left == finalBounds.left &&
+ pipRegionBounds.right == finalBounds.right
+ }
+ .waitForAndVerify()
+ }
+
companion object {
private const val TAG = "PipAppHelper"
private const val ENTER_PIP_BUTTON_ID = "enter_pip"
diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
index e2668bc..0f04d6a 100644
--- a/tests/testables/src/android/testing/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -33,11 +33,15 @@
import android.util.ArrayMap;
import android.view.LayoutInflater;
+import androidx.annotation.Nullable;
+
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
+import java.util.ArrayList;
+
/**
* A ContextWrapper with utilities specifically designed to make Testing easier.
*
@@ -61,6 +65,7 @@
private final TestableContentResolver mTestableContentResolver;
private final TestableSettingsProvider mSettingsProvider;
+ private ArrayList<MockServiceResolver> mMockServiceResolvers;
private ArrayMap<String, Object> mMockSystemServices;
private ArrayMap<ComponentName, IBinder> mMockServices;
private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
@@ -214,12 +219,15 @@
/**
* Adds a mock service to be connected to by a bindService call.
* <p>
- * Normally a TestableContext will pass through all bind requests to the base context
- * but when addMockService has been called for a ComponentName being bound, then
- * TestableContext will immediately trigger a {@link ServiceConnection#onServiceConnected}
- * with the specified service, and will call {@link ServiceConnection#onServiceDisconnected}
- * when the service is unbound.
+ * Normally a TestableContext will pass through all bind requests to the base context
+ * but when addMockService has been called for a ComponentName being bound, then
+ * TestableContext will immediately trigger a {@link ServiceConnection#onServiceConnected}
+ * with the specified service, and will call {@link ServiceConnection#onServiceDisconnected}
+ * when the service is unbound.
* </p>
+ *
+ * @see #addMockServiceResolver(MockServiceResolver) for custom resolution of service Intents to
+ * ComponentNames
*/
public void addMockService(ComponentName component, IBinder service) {
if (mMockServices == null) mMockServices = new ArrayMap<>();
@@ -227,12 +235,38 @@
}
/**
+ * Strategy to resolve a service {@link Intent} to a mock service {@link ComponentName}.
+ */
+ public interface MockServiceResolver {
+ @Nullable
+ ComponentName resolve(Intent service);
+ }
+
+ /**
+ * Registers a strategy to resolve service intents to registered mock services.
+ * <p>
+ * The result of the first {@link MockServiceResolver} to return a non-null
+ * {@link ComponentName} is used to look up a mock service. The mock service must be registered
+ * via {@link #addMockService(ComponentName, IBinder)} separately, using the same component
+ * name.
+ *
+ * If none of the resolvers return a non-null value, or the first returned component name
+ * does not link to a registered mock service, the bind requests are passed to the base context
+ *
+ * The resolvers are queried in order of registration.
+ */
+ public void addMockServiceResolver(MockServiceResolver resolver) {
+ if (mMockServiceResolvers == null) mMockServiceResolvers = new ArrayList<>();
+ mMockServiceResolvers.add(resolver);
+ }
+
+ /**
* @see #addMockService(ComponentName, IBinder)
*/
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
- if (checkMocks(service.getComponent(), conn)) return true;
+ if (checkMocks(service, conn)) return true;
return super.bindService(service, conn, flags);
}
@@ -243,7 +277,7 @@
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
Handler handler, UserHandle user) {
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
- if (checkMocks(service.getComponent(), conn)) return true;
+ if (checkMocks(service, conn)) return true;
return super.bindServiceAsUser(service, conn, flags, handler, user);
}
@@ -254,18 +288,36 @@
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
- if (checkMocks(service.getComponent(), conn)) return true;
+ if (checkMocks(service, conn)) return true;
return super.bindServiceAsUser(service, conn, flags, user);
}
- private boolean checkMocks(ComponentName component, ServiceConnection conn) {
- if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
- if (mActiveServices == null) mActiveServices = new ArrayMap<>();
- mActiveServices.put(conn, component);
- conn.onServiceConnected(component, mMockServices.get(component));
- return true;
+ private boolean checkMocks(Intent service, ServiceConnection conn) {
+ if (mMockServices == null) return false;
+
+ ComponentName serviceComponent = resolveMockServiceComponent(service);
+ if (serviceComponent == null) return false;
+
+ IBinder serviceImpl = mMockServices.get(serviceComponent);
+ if (serviceImpl == null) return false;
+
+ if (mActiveServices == null) mActiveServices = new ArrayMap<>();
+ mActiveServices.put(conn, serviceComponent);
+ conn.onServiceConnected(serviceComponent, serviceImpl);
+ return true;
+ }
+
+ private ComponentName resolveMockServiceComponent(Intent service) {
+ ComponentName specifiedComponentName = service.getComponent();
+ if (specifiedComponentName != null) return specifiedComponentName;
+
+ if (mMockServiceResolvers == null) return null;
+
+ for (MockServiceResolver resolver : mMockServiceResolvers) {
+ ComponentName resolvedComponent = resolver.resolve(service);
+ if (resolvedComponent != null) return resolvedComponent;
}
- return false;
+ return null;
}
/**
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
index d3b7b12..9bfeb63 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
@@ -207,10 +207,14 @@
}
}
- private static void validate(long deviceId, @NetworkType int networkType, String networkName) {
+ private static void validate(long deviceId, @NetworkType int networkType, String networkName,
+ NetworkProviderInfo networkProviderInfo) {
if (deviceId < 0) {
throw new IllegalArgumentException("DeviceId must be set");
}
+ if (Objects.isNull(networkProviderInfo)) {
+ throw new IllegalArgumentException("NetworkProviderInfo must be set");
+ }
if (networkType != NETWORK_TYPE_CELLULAR && networkType != NETWORK_TYPE_WIFI
&& networkType != NETWORK_TYPE_ETHERNET && networkType != NETWORK_TYPE_UNKNOWN) {
throw new IllegalArgumentException("Illegal network type");
@@ -230,7 +234,8 @@
@Nullable @SecurityType ArraySet<Integer> hotspotSecurityTypes) {
validate(deviceId,
networkType,
- networkName);
+ networkName,
+ networkProviderInfo);
mDeviceId = deviceId;
mNetworkProviderInfo = networkProviderInfo;
mNetworkType = networkType;
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 6046415..15fd817 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -161,9 +161,9 @@
Resources resources = context.getResources();
try {
String servicePackageName = resources.getString(
- R.string.shared_connectivity_service_package);
+ R.string.config_sharedConnectivityServicePackage);
String serviceIntentAction = resources.getString(
- R.string.shared_connectivity_service_intent_action);
+ R.string.config_sharedConnectivityServiceIntentAction);
return new SharedConnectivityManager(context, servicePackageName, serviceIntentAction);
} catch (Resources.NotFoundException e) {
Log.e(TAG, "To support shared connectivity service on this device, the service's"
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index c53da9c..57108e4 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -25,6 +25,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
@@ -40,8 +41,11 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.R;
+
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
@@ -382,6 +386,30 @@
}
/**
+ * System and settings UI support on the device for instant tether.
+ * @return True if the UI can display Instant Tether network data. False otherwise.
+ */
+ public static boolean areHotspotNetworksEnabledForService(@NonNull Context context) {
+ String servicePackage = context.getResources()
+ .getString(R.string.config_sharedConnectivityServicePackage);
+ return Objects.equals(context.getPackageName(), servicePackage)
+ && context.getResources()
+ .getBoolean(R.bool.config_hotspotNetworksEnabledForService);
+ }
+
+ /**
+ * System and settings UI support on the device for known networks.
+ * @return True if the UI can display known networks data. False otherwise.
+ */
+ public static boolean areKnownNetworksEnabledForService(@NonNull Context context) {
+ String servicePackage = context.getResources()
+ .getString(R.string.config_sharedConnectivityServicePackage);
+ return Objects.equals(context.getPackageName(), servicePackage)
+ && context.getResources()
+ .getBoolean(R.bool.config_knownNetworksEnabledForService);
+ }
+
+ /**
* Implementing application should implement this method.
*
* Implementation should initiate a connection to the Hotspot Network indicated.
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
index 8e396b6..8302094 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
@@ -26,12 +26,16 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
import android.os.Parcel;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.Arrays;
@@ -39,6 +43,7 @@
* Unit tests for {@link HotspotNetwork}.
*/
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class HotspotNetworkTest {
private static final long DEVICE_ID = 11L;
private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
@@ -67,7 +72,7 @@
*/
@Test
public void testParcelOperation() {
- HotspotNetwork network = buildHotspotNetworkBuilder().build();
+ HotspotNetwork network = buildHotspotNetworkBuilder(true).build();
Parcel parcelW = Parcel.obtain();
network.writeToParcel(parcelW, 0);
@@ -88,30 +93,30 @@
*/
@Test
public void testEqualsOperation() {
- HotspotNetwork network1 = buildHotspotNetworkBuilder().build();
- HotspotNetwork network2 = buildHotspotNetworkBuilder().build();
+ HotspotNetwork network1 = buildHotspotNetworkBuilder(true).build();
+ HotspotNetwork network2 = buildHotspotNetworkBuilder(true).build();
assertThat(network1).isEqualTo(network2);
- HotspotNetwork.Builder builder = buildHotspotNetworkBuilder().setDeviceId(DEVICE_ID_1);
+ HotspotNetwork.Builder builder = buildHotspotNetworkBuilder(true).setDeviceId(DEVICE_ID_1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildHotspotNetworkBuilder().setNetworkProviderInfo(NETWORK_PROVIDER_INFO1);
+ builder = buildHotspotNetworkBuilder(true).setNetworkProviderInfo(NETWORK_PROVIDER_INFO1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildHotspotNetworkBuilder().setHostNetworkType(NETWORK_TYPE_1);
+ builder = buildHotspotNetworkBuilder(true).setHostNetworkType(NETWORK_TYPE_1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildHotspotNetworkBuilder().setNetworkName(NETWORK_NAME_1);
+ builder = buildHotspotNetworkBuilder(true).setNetworkName(NETWORK_NAME_1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildHotspotNetworkBuilder().setHotspotSsid(HOTSPOT_SSID_1);
+ builder = buildHotspotNetworkBuilder(true).setHotspotSsid(HOTSPOT_SSID_1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildHotspotNetworkBuilder().setHotspotBssid(HOTSPOT_BSSID_1);
+ builder = buildHotspotNetworkBuilder(true).setHotspotBssid(HOTSPOT_BSSID_1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildHotspotNetworkBuilder();
- HotspotNetwork.Builder builder1 = buildHotspotNetworkBuilder();
+ builder = buildHotspotNetworkBuilder(true);
+ HotspotNetwork.Builder builder1 = buildHotspotNetworkBuilder(true);
Arrays.stream(HOTSPOT_SECURITY_TYPES_1).forEach(builder1::addHotspotSecurityType);
assertThat(builder1.build()).isNotEqualTo(builder.build());
@@ -122,7 +127,7 @@
*/
@Test
public void testGetMethods() {
- HotspotNetwork network = buildHotspotNetworkBuilder().build();
+ HotspotNetwork network = buildHotspotNetworkBuilder(true).build();
ArraySet<Integer> securityTypes = new ArraySet<>();
Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(securityTypes::add);
@@ -137,21 +142,30 @@
@Test
public void testHashCode() {
- HotspotNetwork network1 = buildHotspotNetworkBuilder().build();
- HotspotNetwork network2 = buildHotspotNetworkBuilder().build();
+ HotspotNetwork network1 = buildHotspotNetworkBuilder(true).build();
+ HotspotNetwork network2 = buildHotspotNetworkBuilder(true).build();
assertThat(network1.hashCode()).isEqualTo(network2.hashCode());
}
- private HotspotNetwork.Builder buildHotspotNetworkBuilder() {
+ @Test
+ public void networkProviderInfoNotSet_shouldThrowException() {
+ Exception e = assertThrows(IllegalArgumentException.class,
+ () -> buildHotspotNetworkBuilder(false).build());
+ assertThat(e.getMessage()).contains("NetworkProviderInfo");
+ }
+
+ private HotspotNetwork.Builder buildHotspotNetworkBuilder(boolean withNetworkProviderInfo) {
HotspotNetwork.Builder builder = new HotspotNetwork.Builder()
.setDeviceId(DEVICE_ID)
- .setNetworkProviderInfo(NETWORK_PROVIDER_INFO)
.setHostNetworkType(NETWORK_TYPE)
.setNetworkName(NETWORK_NAME)
.setHotspotSsid(HOTSPOT_SSID)
.setHotspotBssid(HOTSPOT_BSSID);
Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
+ if (withNetworkProviderInfo) {
+ builder.setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
+ }
return builder;
}
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
index 19effe5..b8b6b767 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -26,10 +26,12 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus;
import android.net.wifi.sharedconnectivity.app.KnownNetwork;
@@ -86,6 +88,9 @@
@Mock
Context mContext;
+ @Mock
+ Resources mResources;
+
static class FakeSharedConnectivityService extends SharedConnectivityService {
public void attachBaseContext(Context context) {
super.attachBaseContext(context);
@@ -180,6 +185,48 @@
.isEqualTo(KNOWN_NETWORK_CONNECTION_STATUS);
}
+ @Test
+ public void areHotspotNetworksEnabledForService() {
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getPackageName()).thenReturn("package");
+ when(mResources.getString(anyInt())).thenReturn("package");
+ when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+ assertThat(SharedConnectivityService.areHotspotNetworksEnabledForService(mContext))
+ .isTrue();
+ }
+
+ @Test
+ public void areHotspotNetworksEnabledForService_notSamePackage_shouldReturnFalse() {
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getPackageName()).thenReturn("package");
+ when(mResources.getString(anyInt())).thenReturn("other_package");
+ when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+ assertThat(SharedConnectivityService.areHotspotNetworksEnabledForService(mContext))
+ .isFalse();
+ }
+
+ @Test
+ public void areKnownNetworksEnabledForService() {
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getPackageName()).thenReturn("package");
+ when(mResources.getString(anyInt())).thenReturn("package");
+ when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+ assertThat(SharedConnectivityService.areKnownNetworksEnabledForService(mContext)).isTrue();
+ }
+
+ @Test
+ public void areKnownNetworksEnabledForService_notSamePackage_shouldReturnFalse() {
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getPackageName()).thenReturn("package");
+ when(mResources.getString(anyInt())).thenReturn("other_package");
+ when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+ assertThat(SharedConnectivityService.areKnownNetworksEnabledForService(mContext)).isFalse();
+ }
+
private SharedConnectivityService createService() {
FakeSharedConnectivityService service = new FakeSharedConnectivityService();
service.attachBaseContext(mContext);