diff options
141 files changed, 3227 insertions, 3325 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 02de1cd9a137..771d56cd1987 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6623,6 +6623,7 @@ package android.app { ctor public Notification.MediaStyle(); ctor @Deprecated public Notification.MediaStyle(android.app.Notification.Builder); method public android.app.Notification.MediaStyle setMediaSession(android.media.session.MediaSession.Token); + method @NonNull @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public android.app.Notification.MediaStyle setRemotePlaybackInfo(@NonNull CharSequence, @DrawableRes int, @Nullable android.app.PendingIntent); method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...); } @@ -8049,6 +8050,7 @@ package android.app.admin { field public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; field public static final String ACTION_CHECK_POLICY_COMPLIANCE = "android.app.action.CHECK_POLICY_COMPLIANCE"; field public static final String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE"; + field public static final String ACTION_DEVICE_FINANCING_STATE_CHANGED = "android.app.admin.action.DEVICE_FINANCING_STATE_CHANGED"; field public static final String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED"; field public static final String ACTION_DEVICE_POLICY_RESOURCE_UPDATED = "android.app.action.DEVICE_POLICY_RESOURCE_UPDATED"; field public static final String ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE"; @@ -27478,7 +27480,7 @@ package android.media.tv { method public boolean onTrackballEvent(android.view.MotionEvent); method public abstract boolean onTune(android.net.Uri); method public boolean onTune(android.net.Uri, android.os.Bundle); - method public void onTvMessage(@NonNull String, @NonNull android.os.Bundle); + method public void onTvMessage(int, @NonNull android.os.Bundle); method public void onUnblockContent(android.media.tv.TvContentRating); method public void setOverlayViewEnabled(boolean); } @@ -53907,6 +53909,14 @@ package android.view { method public void removeViewImmediate(android.view.View); field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"; field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"; + field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION"; + field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH"; + field public static final String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE"; + field public static final String PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE"; + field public static final String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE"; + field public static final String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS = "android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS"; + field public static final String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS"; + field public static final String PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION = "android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION"; } public static class WindowManager.BadTokenException extends java.lang.RuntimeException { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 0a893f05e91c..ce5723b0aa3d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -968,10 +968,6 @@ package android.app { field public static final int SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY = 11; // 0xb } - public static class Notification.MediaStyle extends android.app.Notification.Style { - method @NonNull @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public android.app.Notification.MediaStyle setRemotePlaybackInfo(@NonNull CharSequence, @DrawableRes int, @Nullable android.app.PendingIntent); - } - public static final class Notification.TvExtender implements android.app.Notification.Extender { ctor public Notification.TvExtender(); ctor public Notification.TvExtender(android.app.Notification); @@ -9984,6 +9980,7 @@ package android.net.wifi.sharedconnectivity.app { public final class HotspotNetwork implements android.os.Parcelable { method public int describeContents(); method public long getDeviceId(); + method @NonNull public android.os.Bundle getExtras(); method public int getHostNetworkType(); method @Nullable public String getHotspotBssid(); method @NonNull public java.util.Set<java.lang.Integer> getHotspotSecurityTypes(); @@ -10003,6 +10000,7 @@ package android.net.wifi.sharedconnectivity.app { method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder addHotspotSecurityType(int); method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork build(); method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setDeviceId(long); + method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setExtras(@NonNull android.os.Bundle); method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setHostNetworkType(int); method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setHotspotBssid(@NonNull String); method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setHotspotSsid(@NonNull String); @@ -10039,6 +10037,7 @@ package android.net.wifi.sharedconnectivity.app { public final class KnownNetwork implements android.os.Parcelable { method public int describeContents(); + method @NonNull public android.os.Bundle getExtras(); method @Nullable public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo getNetworkProviderInfo(); method public int getNetworkSource(); method @NonNull public java.util.Set<java.lang.Integer> getSecurityTypes(); @@ -10054,6 +10053,7 @@ package android.net.wifi.sharedconnectivity.app { ctor public KnownNetwork.Builder(); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder addSecurityType(int); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork build(); + method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setExtras(@NonNull android.os.Bundle); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setNetworkProviderInfo(@Nullable android.net.wifi.sharedconnectivity.app.NetworkProviderInfo); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setNetworkSource(int); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSsid(@NonNull String); @@ -10085,6 +10085,7 @@ package android.net.wifi.sharedconnectivity.app { method @IntRange(from=0, to=3) public int getConnectionStrength(); method @NonNull public String getDeviceName(); method public int getDeviceType(); + method @NonNull public android.os.Bundle getExtras(); method @NonNull public String getModelName(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.NetworkProviderInfo> CREATOR; @@ -10103,6 +10104,7 @@ package android.net.wifi.sharedconnectivity.app { method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setConnectionStrength(@IntRange(from=0, to=3) int); method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setDeviceName(@NonNull String); method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setDeviceType(int); + method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setExtras(@NonNull android.os.Bundle); method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setModelName(@NonNull String); } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 445b957a44f3..6bfaa352f3f7 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -516,6 +516,10 @@ package android.app { package android.app.admin { + public final class AccountTypePolicyKey extends android.app.admin.PolicyKey { + ctor public AccountTypePolicyKey(@NonNull String, @NonNull String); + } + public final class DeviceAdminAuthority extends android.app.admin.Authority { field @NonNull public static final android.app.admin.DeviceAdminAuthority DEVICE_ADMIN_AUTHORITY; } @@ -612,6 +616,10 @@ package android.app.admin { field @NonNull public static final android.app.admin.FlagUnion FLAG_UNION; } + public final class IntentFilterPolicyKey extends android.app.admin.PolicyKey { + ctor public IntentFilterPolicyKey(@NonNull String, @NonNull android.content.IntentFilter); + } + public final class MostRecent<V> extends android.app.admin.ResolutionMechanism<V> { ctor public MostRecent(); method public int describeContents(); @@ -627,6 +635,14 @@ package android.app.admin { field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.MostRestrictive<?>> CREATOR; } + public final class PackagePermissionPolicyKey extends android.app.admin.PolicyKey { + ctor public PackagePermissionPolicyKey(@NonNull String, @NonNull String, @NonNull String); + } + + public final class PackagePolicyKey extends android.app.admin.PolicyKey { + ctor public PackagePolicyKey(@NonNull String, @NonNull String); + } + public final class PolicyState<V> implements android.os.Parcelable { method @NonNull public android.app.admin.ResolutionMechanism<V> getResolutionMechanism(); } @@ -676,6 +692,10 @@ package android.app.admin { method public int getOperation(); } + public final class UserRestrictionPolicyKey extends android.app.admin.PolicyKey { + ctor public UserRestrictionPolicyKey(@NonNull String, @NonNull String); + } + } package android.app.assist { diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java index a81ef18c8022..d0ce70133414 100644 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -69,7 +69,7 @@ public abstract class Animator implements Cloneable { * backing field for backgroundPauseDelay property. This could be simply a hardcoded * value in AnimationHandler, but it is useful to be able to change the value in tests. */ - private static long sBackgroundPauseDelay = 10000; + private static long sBackgroundPauseDelay = 1000; /** * Sets the duration for delaying pausing animators when apps go into the background. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 2751b54ebdb7..682fec8105d5 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5840,26 +5840,6 @@ public final class ActivityThread extends ClientTransactionHandler r.activity.mChangingConfigurations = true; - // If we are preserving the main window across relaunches we would also like to preserve - // the children. However the client side view system does not support preserving - // the child views so we notify the window manager to expect these windows to - // be replaced and defer requests to destroy or hide them. This way we can achieve - // visual continuity. It's important that we do this here prior to pause and destroy - // as that is when we may hide or remove the child views. - // - // There is another scenario, if we have decided locally to relaunch the app from a - // call to recreate, then none of the windows will be prepared for replacement or - // preserved by the server, so we want to notify it that we are preparing to replace - // everything - try { - if (r.mPreserveWindow) { - WindowManagerGlobal.getWindowSession().prepareToReplaceWindows( - r.token, true /* childrenOnly */); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents, pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity"); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index e78fb179eb6c..502ef0db586b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -4634,9 +4634,9 @@ public class Notification implements Parcelable * Set whether this is an "ongoing" notification. * * Ongoing notifications cannot be dismissed by the user on locked devices, or by - * notification listeners, and some notifications (device management, media) cannot be - * dismissed on unlocked devices, so your application or service must take - * care of canceling them. + * notification listeners, and some notifications (call, device management, media) cannot + * be dismissed on unlocked devices, so your application or service must take care of + * canceling them. * * They are typically used to indicate a background task that the user is actively engaged * with (e.g., playing music) or is pending in some way and therefore occupying the device @@ -9163,10 +9163,7 @@ public class Notification implements Parcelable * {@code null}, in which case the output switcher will be disabled. * This intent should open an Activity or it will be ignored. * @return MediaStyle - * - * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) @NonNull public MediaStyle setRemotePlaybackInfo(@NonNull CharSequence deviceName, diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java index 6417cd47b504..9e376a7e2bee 100644 --- a/core/java/android/app/admin/AccountTypePolicyKey.java +++ b/core/java/android/app/admin/AccountTypePolicyKey.java @@ -23,6 +23,7 @@ import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Bundle; import android.os.Parcel; @@ -49,6 +50,7 @@ public final class AccountTypePolicyKey extends PolicyKey { /** * @hide */ + @TestApi public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) { super(key); mAccountType = Objects.requireNonNull((accountType)); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 024141777604..6bbbfe1ef4b0 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -57,6 +57,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.Manifest.permission; import android.accounts.Account; +import android.annotation.BroadcastBehavior; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; import android.annotation.IntDef; @@ -3998,6 +3999,27 @@ public class DevicePolicyManager { public static final String EXTRA_RESOURCE_IDS = "android.app.extra.RESOURCE_IDS"; + /** + * Broadcast Action: Broadcast sent to indicate that the device financing state has changed. + * + * <p>This occurs when, for example, a financing kiosk app has been added or removed. + * + * <p>To query the current device financing state see {@link #isDeviceFinanced}. + * + * <p>This will be delivered to the following apps if they include a receiver for this action + * in their manifest: + * <ul> + * <li>Device owner admins. + * <li>Organization-owned profile owner admins + * <li>The supervision app + * <li>The device management role holder + * </ul> + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + @BroadcastBehavior(explicitOnly = true, includeBackground = true) + public static final String ACTION_DEVICE_FINANCING_STATE_CHANGED = + "android.app.admin.action.DEVICE_FINANCING_STATE_CHANGED"; + /** Allow the user to choose whether to enable MTE on the device. */ public static final int MTE_NOT_CONTROLLED_BY_POLICY = 0; diff --git a/core/java/android/app/admin/IntentFilterPolicyKey.java b/core/java/android/app/admin/IntentFilterPolicyKey.java index b0af4cd77749..30aad965c008 100644 --- a/core/java/android/app/admin/IntentFilterPolicyKey.java +++ b/core/java/android/app/admin/IntentFilterPolicyKey.java @@ -23,6 +23,7 @@ import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.content.IntentFilter; import android.os.Bundle; import android.os.Parcel; @@ -49,6 +50,7 @@ public final class IntentFilterPolicyKey extends PolicyKey { /** * @hide */ + @TestApi public IntentFilterPolicyKey(@NonNull String identifier, @NonNull IntentFilter filter) { super(identifier); mFilter = Objects.requireNonNull(filter); diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java index 08c4224021f1..7fd514cebd7c 100644 --- a/core/java/android/app/admin/PackagePermissionPolicyKey.java +++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java @@ -24,6 +24,7 @@ import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -53,6 +54,7 @@ public final class PackagePermissionPolicyKey extends PolicyKey { /** * @hide */ + @TestApi public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName, @NonNull String permissionName) { super(identifier); diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java index b2a8d5d24b84..2ab00bc3146c 100644 --- a/core/java/android/app/admin/PackagePolicyKey.java +++ b/core/java/android/app/admin/PackagePolicyKey.java @@ -23,6 +23,7 @@ import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -50,6 +51,7 @@ public final class PackagePolicyKey extends PolicyKey { /** * @hide */ + @TestApi public PackagePolicyKey(@NonNull String key, @NonNull String packageName) { super(key); mPackageName = Objects.requireNonNull((packageName)); diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java index 880b58b8c321..aeb238041605 100644 --- a/core/java/android/app/admin/UserRestrictionPolicyKey.java +++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java @@ -20,6 +20,7 @@ import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Bundle; import android.os.Parcel; @@ -40,6 +41,7 @@ public final class UserRestrictionPolicyKey extends PolicyKey { /** * @hide */ + @TestApi public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) { super(identifier); mRestriction = Objects.requireNonNull(restriction); diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index fe10b7f8b3f4..27f6a266597c 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -31,6 +31,7 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PointF; import android.graphics.Rect; @@ -311,20 +312,27 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW super.onLayout(changed, left, top, right, bottom); } catch (final RuntimeException e) { Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e); - removeViewInLayout(mView); - View child = getErrorView(); - prepareView(child); - addViewInLayout(child, 0, child.getLayoutParams()); - measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, - child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); - mView = child; - mViewMode = VIEW_MODE_ERROR; + handleViewError(); } } /** + * Remove bad view and replace with error message view + */ + private void handleViewError() { + removeViewInLayout(mView); + View child = getErrorView(); + prepareView(child); + addViewInLayout(child, 0, child.getLayoutParams()); + measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); + child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, + child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); + mView = child; + mViewMode = VIEW_MODE_ERROR; + } + + /** * Provide guidance about the size of this widget to the AppWidgetManager. The widths and * heights should correspond to the full area the AppWidgetHostView is given. Padding added by * the framework will be accounted for automatically. This information gets embedded into the @@ -953,4 +961,15 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW reapplyLastRemoteViews(); } } + + @Override + protected void dispatchDraw(@NonNull Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + post(this::handleViewError); + } + } } diff --git a/core/java/android/hardware/input/InputDeviceLightsManager.java b/core/java/android/hardware/input/InputDeviceLightsManager.java index 802e6dde497a..f4ee9a21c42c 100644 --- a/core/java/android/hardware/input/InputDeviceLightsManager.java +++ b/core/java/android/hardware/input/InputDeviceLightsManager.java @@ -18,6 +18,7 @@ package android.hardware.input; import android.annotation.NonNull; import android.app.ActivityThread; +import android.content.Context; import android.hardware.lights.Light; import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; @@ -30,22 +31,22 @@ import java.lang.ref.Reference; import java.util.List; /** - * LightsManager manages an input device's lights {@link android.hardware.input.Light}. + * LightsManager manages an input device's lights {@link android.hardware.lights.Light} */ class InputDeviceLightsManager extends LightsManager { private static final String TAG = "InputDeviceLightsManager"; private static final boolean DEBUG = false; - private final InputManager mInputManager; + private final InputManagerGlobal mGlobal; // The input device ID. private final int mDeviceId; // Package name private final String mPackageName; - InputDeviceLightsManager(InputManager inputManager, int deviceId) { - super(ActivityThread.currentActivityThread().getSystemContext()); - mInputManager = inputManager; + InputDeviceLightsManager(Context context, int deviceId) { + super(context); + mGlobal = InputManagerGlobal.getInstance(); mDeviceId = deviceId; mPackageName = ActivityThread.currentPackageName(); } @@ -57,7 +58,7 @@ class InputDeviceLightsManager extends LightsManager { */ @Override public @NonNull List<Light> getLights() { - return mInputManager.getLights(mDeviceId); + return mGlobal.getLights(mDeviceId); } /** @@ -68,7 +69,7 @@ class InputDeviceLightsManager extends LightsManager { @Override public @NonNull LightState getLightState(@NonNull Light light) { Preconditions.checkNotNull(light); - return mInputManager.getLightState(mDeviceId, light); + return mGlobal.getLightState(mDeviceId, light); } /** @@ -77,7 +78,7 @@ class InputDeviceLightsManager extends LightsManager { @Override public @NonNull LightsSession openSession() { final LightsSession session = new InputDeviceLightsSession(); - mInputManager.openLightSession(mDeviceId, mPackageName, session.getToken()); + mGlobal.openLightSession(mDeviceId, mPackageName, session.getToken()); return session; } @@ -113,7 +114,7 @@ class InputDeviceLightsManager extends LightsManager { Preconditions.checkNotNull(request); Preconditions.checkArgument(!mClosed); - mInputManager.requestLights(mDeviceId, request, getToken()); + mGlobal.requestLights(mDeviceId, request, getToken()); } /** @@ -122,7 +123,7 @@ class InputDeviceLightsManager extends LightsManager { @Override public void close() { if (!mClosed) { - mInputManager.closeLightSession(mDeviceId, getToken()); + mGlobal.closeLightSession(mDeviceId, getToken()); mClosed = true; mCloseGuard.close(); } diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java index ce6b52391f12..9c1826071822 100644 --- a/core/java/android/hardware/input/InputDeviceVibrator.java +++ b/core/java/android/hardware/input/InputDeviceVibrator.java @@ -45,14 +45,14 @@ final class InputDeviceVibrator extends Vibrator { private final int mDeviceId; private final VibratorInfo mVibratorInfo; private final Binder mToken; - private final InputManager mInputManager; + private final InputManagerGlobal mGlobal; @GuardedBy("mDelegates") private final ArrayMap<OnVibratorStateChangedListener, OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>(); - InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) { - mInputManager = inputManager; + InputDeviceVibrator(int deviceId, int vibratorId) { + mGlobal = InputManagerGlobal.getInstance(); mDeviceId = deviceId; mVibratorInfo = new VibratorInfo.Builder(vibratorId) .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) @@ -93,7 +93,7 @@ final class InputDeviceVibrator extends Vibrator { @Override public boolean isVibrating() { - return mInputManager.isVibrating(mDeviceId); + return mGlobal.isVibrating(mDeviceId); } /** @@ -132,7 +132,7 @@ final class InputDeviceVibrator extends Vibrator { final OnVibratorStateChangedListenerDelegate delegate = new OnVibratorStateChangedListenerDelegate(listener, executor); - if (!mInputManager.registerVibratorStateListener(mDeviceId, delegate)) { + if (!mGlobal.registerVibratorStateListener(mDeviceId, delegate)) { Log.w(TAG, "Failed to register vibrate state listener"); return; } @@ -156,7 +156,7 @@ final class InputDeviceVibrator extends Vibrator { if (mDelegates.containsKey(listener)) { final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener); - if (!mInputManager.unregisterVibratorStateListener(mDeviceId, delegate)) { + if (!mGlobal.unregisterVibratorStateListener(mDeviceId, delegate)) { Log.w(TAG, "Failed to unregister vibrate state listener"); return; } @@ -176,12 +176,12 @@ final class InputDeviceVibrator extends Vibrator { @Override public void vibrate(int uid, String opPkg, @NonNull VibrationEffect effect, String reason, @NonNull VibrationAttributes attributes) { - mInputManager.vibrate(mDeviceId, effect, mToken); + mGlobal.vibrate(mDeviceId, effect, mToken); } @Override public void cancel() { - mInputManager.cancelVibrate(mDeviceId, mToken); + mGlobal.cancelVibrate(mDeviceId, mToken); } @Override diff --git a/core/java/android/hardware/input/InputDeviceVibratorManager.java b/core/java/android/hardware/input/InputDeviceVibratorManager.java index d77f9c351b8e..64b566772884 100644 --- a/core/java/android/hardware/input/InputDeviceVibratorManager.java +++ b/core/java/android/hardware/input/InputDeviceVibratorManager.java @@ -40,7 +40,7 @@ public class InputDeviceVibratorManager extends VibratorManager private static final boolean DEBUG = false; private final Binder mToken; - private final InputManager mInputManager; + private final InputManagerGlobal mGlobal; // The input device Id. private final int mDeviceId; @@ -48,8 +48,8 @@ public class InputDeviceVibratorManager extends VibratorManager @GuardedBy("mVibrators") private final SparseArray<Vibrator> mVibrators = new SparseArray<>(); - public InputDeviceVibratorManager(InputManager inputManager, int deviceId) { - mInputManager = inputManager; + public InputDeviceVibratorManager(int deviceId) { + mGlobal = InputManagerGlobal.getInstance(); mDeviceId = deviceId; mToken = new Binder(); @@ -61,10 +61,10 @@ public class InputDeviceVibratorManager extends VibratorManager mVibrators.clear(); InputDevice inputDevice = InputDevice.getDevice(mDeviceId); final int[] vibratorIds = - mInputManager.getVibratorIds(mDeviceId); + mGlobal.getVibratorIds(mDeviceId); for (int i = 0; i < vibratorIds.length; i++) { mVibrators.put(vibratorIds[i], - new InputDeviceVibrator(mInputManager, mDeviceId, vibratorIds[i])); + new InputDeviceVibrator(mDeviceId, vibratorIds[i])); } } } @@ -127,12 +127,12 @@ public class InputDeviceVibratorManager extends VibratorManager @Override public void vibrate(int uid, String opPkg, @NonNull CombinedVibration effect, String reason, @Nullable VibrationAttributes attributes) { - mInputManager.vibrate(mDeviceId, effect, mToken); + mGlobal.vibrate(mDeviceId, effect, mToken); } @Override public void cancel() { - mInputManager.cancelVibrate(mDeviceId, mToken); + mGlobal.cancelVibrate(mDeviceId, mToken); } @Override diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 054ae21be14a..5dc3825215c0 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -33,21 +33,15 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.hardware.BatteryState; import android.hardware.SensorManager; -import android.hardware.lights.Light; -import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; -import android.hardware.lights.LightsRequest; import android.os.Binder; import android.os.Build; -import android.os.CombinedVibration; import android.os.Handler; import android.os.IBinder; -import android.os.IVibratorStateListener; import android.os.InputEventInjectionSync; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; -import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorManager; import android.util.Log; @@ -1380,7 +1374,7 @@ public final class InputManager { * @hide */ public Vibrator getInputDeviceVibrator(int deviceId, int vibratorId) { - return new InputDeviceVibrator(this, deviceId, vibratorId); + return new InputDeviceVibrator(deviceId, vibratorId); } /** @@ -1391,85 +1385,7 @@ public final class InputManager { */ @NonNull public VibratorManager getInputDeviceVibratorManager(int deviceId) { - return new InputDeviceVibratorManager(InputManager.this, deviceId); - } - - /* - * Get the list of device vibrators - * @return The list of vibrators IDs - */ - int[] getVibratorIds(int deviceId) { - try { - return mIm.getVibratorIds(deviceId); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - } - - /* - * Perform vibration effect - */ - void vibrate(int deviceId, VibrationEffect effect, IBinder token) { - try { - mIm.vibrate(deviceId, effect, token); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - } - - /* - * Perform combined vibration effect - */ - void vibrate(int deviceId, CombinedVibration effect, IBinder token) { - try { - mIm.vibrateCombined(deviceId, effect, token); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - } - - /* - * Cancel an ongoing vibration - */ - void cancelVibrate(int deviceId, IBinder token) { - try { - mIm.cancelVibrate(deviceId, token); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - } - - /* - * Check if input device is vibrating - */ - boolean isVibrating(int deviceId) { - try { - return mIm.isVibrating(deviceId); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - } - - /** - * Register input device vibrator state listener - */ - boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { - try { - return mIm.registerVibratorStateListener(deviceId, listener); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - } - - /** - * Unregister input device vibrator state listener - */ - boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) { - try { - return mIm.unregisterVibratorStateListener(deviceId, listener); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + return new InputDeviceVibratorManager(deviceId); } /** @@ -1499,77 +1415,7 @@ public final class InputManager { */ @NonNull public LightsManager getInputDeviceLightsManager(int deviceId) { - return new InputDeviceLightsManager(InputManager.this, deviceId); - } - - /** - * Gets a list of light objects associated with an input device. - * @return The list of lights, never null. - */ - @NonNull List<Light> getLights(int deviceId) { - try { - return mIm.getLights(deviceId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Returns the state of an input device light. - * @return the light state - */ - @NonNull LightState getLightState(int deviceId, @NonNull Light light) { - try { - return mIm.getLightState(deviceId, light.getId()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Request to modify the states of multiple lights. - * - * @param request the settings for lights that should change - */ - void requestLights(int deviceId, @NonNull LightsRequest request, IBinder token) { - try { - List<Integer> lightIdList = request.getLights(); - int[] lightIds = new int[lightIdList.size()]; - for (int i = 0; i < lightIds.length; i++) { - lightIds[i] = lightIdList.get(i); - } - List<LightState> lightStateList = request.getLightStates(); - mIm.setLightStates(deviceId, lightIds, - lightStateList.toArray(new LightState[0]), - token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Open light session for input device manager - * - * @param token The token for the light session - */ - void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) { - try { - mIm.openLightSession(deviceId, opPkg, token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Close light session - * - */ - void closeLightSession(int deviceId, @NonNull IBinder token) { - try { - mIm.closeLightSession(deviceId, token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return new InputDeviceLightsManager(getContext(), deviceId); } /** diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java index 524d8206bddf..08d81bd3c325 100644 --- a/core/java/android/hardware/input/InputManagerGlobal.java +++ b/core/java/android/hardware/input/InputManagerGlobal.java @@ -27,12 +27,18 @@ import android.hardware.input.InputManager.InputDeviceBatteryListener; import android.hardware.input.InputManager.InputDeviceListener; import android.hardware.input.InputManager.KeyboardBacklightListener; import android.hardware.input.InputManager.OnTabletModeChangedListener; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; +import android.hardware.lights.LightsRequest; +import android.os.CombinedVibration; import android.os.Handler; import android.os.IBinder; +import android.os.IVibratorStateListener; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.VibrationEffect; import android.util.Log; import android.util.SparseArray; import android.view.Display; @@ -903,4 +909,152 @@ public final class InputManagerGlobal { throw ex.rethrowFromSystemServer(); } } + + /** + * Gets a list of light objects associated with an input device. + * @return The list of lights, never null. + */ + @NonNull List<Light> getLights(int deviceId) { + try { + return mIm.getLights(deviceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the state of an input device light. + * @return the light state + */ + @NonNull LightState getLightState(int deviceId, @NonNull Light light) { + try { + return mIm.getLightState(deviceId, light.getId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Request to modify the states of multiple lights. + * + * @param request the settings for lights that should change + */ + void requestLights(int deviceId, @NonNull LightsRequest request, IBinder token) { + try { + List<Integer> lightIdList = request.getLights(); + int[] lightIds = new int[lightIdList.size()]; + for (int i = 0; i < lightIds.length; i++) { + lightIds[i] = lightIdList.get(i); + } + List<LightState> lightStateList = request.getLightStates(); + mIm.setLightStates(deviceId, lightIds, + lightStateList.toArray(new LightState[0]), + token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Open light session for input device manager + * + * @param token The token for the light session + */ + void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) { + try { + mIm.openLightSession(deviceId, opPkg, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Close light session + * + */ + void closeLightSession(int deviceId, @NonNull IBinder token) { + try { + mIm.closeLightSession(deviceId, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /* + * Get the list of device vibrators + * @return The list of vibrators IDs + */ + int[] getVibratorIds(int deviceId) { + try { + return mIm.getVibratorIds(deviceId); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /* + * Perform vibration effect + */ + void vibrate(int deviceId, VibrationEffect effect, IBinder token) { + try { + mIm.vibrate(deviceId, effect, token); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /* + * Perform combined vibration effect + */ + void vibrate(int deviceId, CombinedVibration effect, IBinder token) { + try { + mIm.vibrateCombined(deviceId, effect, token); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /* + * Cancel an ongoing vibration + */ + void cancelVibrate(int deviceId, IBinder token) { + try { + mIm.cancelVibrate(deviceId, token); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /* + * Check if input device is vibrating + */ + boolean isVibrating(int deviceId) { + try { + return mIm.isVibrating(deviceId); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Register input device vibrator state listener + */ + boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { + try { + return mIm.registerVibratorStateListener(deviceId, listener); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Unregister input device vibrator state listener + */ + boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) { + try { + return mIm.unregisterVibratorStateListener(deviceId, listener); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 127c7a0476f6..05b6da4f7cbe 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11433,21 +11433,46 @@ public final class Settings { public @interface DeviceStateRotationLockSetting { } + /** @hide */ + public static final int DEVICE_STATE_ROTATION_KEY_UNKNOWN = -1; + /** @hide */ + public static final int DEVICE_STATE_ROTATION_KEY_FOLDED = 0; + /** @hide */ + public static final int DEVICE_STATE_ROTATION_KEY_HALF_FOLDED = 1; + /** @hide */ + public static final int DEVICE_STATE_ROTATION_KEY_UNFOLDED = 2; + + /** + * The different postures that can be used as keys with + * {@link #DEVICE_STATE_ROTATION_LOCK}. + * @hide + */ + @IntDef(prefix = {"DEVICE_STATE_ROTATION_KEY_"}, value = { + DEVICE_STATE_ROTATION_KEY_UNKNOWN, + DEVICE_STATE_ROTATION_KEY_FOLDED, + DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, + DEVICE_STATE_ROTATION_KEY_UNFOLDED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DeviceStateRotationLockKey { + } + /** * Rotation lock setting keyed on device state. * - * This holds a serialized map using int keys that represent Device States and value of + * This holds a serialized map using int keys that represent postures in + * {@link DeviceStateRotationLockKey} and value of * {@link DeviceStateRotationLockSetting} representing the rotation lock setting for that - * device state. + * posture. * * Serialized as key0:value0:key1:value1:...:keyN:valueN. * * Example: "0:1:1:2:2:1" * This example represents a map of: * <ul> - * <li>0 -> DEVICE_STATE_ROTATION_LOCK_LOCKED</li> - * <li>1 -> DEVICE_STATE_ROTATION_LOCK_UNLOCKED</li> - * <li>2 -> DEVICE_STATE_ROTATION_LOCK_IGNORED</li> + * <li>DEVICE_STATE_ROTATION_KEY_FOLDED -> DEVICE_STATE_ROTATION_LOCK_LOCKED</li> + * <li>DEVICE_STATE_ROTATION_KEY_HALF_FOLDED -> DEVICE_STATE_ROTATION_LOCK_UNLOCKED</li> + * <li>DEVICE_STATE_ROTATION_KEY_UNFOLDED -> DEVICE_STATE_ROTATION_LOCK_IGNORED</li> * </ul> * * @hide diff --git a/core/java/android/security/net/config/SystemCertificateSource.java b/core/java/android/security/net/config/SystemCertificateSource.java index 13f7e5d4232b..3a254c1d92fc 100644 --- a/core/java/android/security/net/config/SystemCertificateSource.java +++ b/core/java/android/security/net/config/SystemCertificateSource.java @@ -39,9 +39,13 @@ public final class SystemCertificateSource extends DirectoryCertificateSource { } private static File getDirectory() { - // TODO(miguelaranda): figure out correct code path. + if ((System.getProperty("system.certs.enabled") != null) + && (System.getProperty("system.certs.enabled")).equals("true")) { + return new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts"); + } File updatable_dir = new File("/apex/com.android.conscrypt/cacerts"); - if (updatable_dir.exists() && !(updatable_dir.list().length == 0)) { + if (updatable_dir.exists() + && !(updatable_dir.list().length == 0)) { return updatable_dir; } return new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts"); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 5810642402a3..83de2a0fafbe 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -110,16 +110,6 @@ interface IWindowSession { int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq, int lastSyncSeqId); - /* - * Notify the window manager that an application is relaunching and - * windows should be prepared for replacement. - * - * @param appToken The application - * @param childrenOnly Whether to only prepare child windows for replacement - * (for example when main windows are being reused via preservation). - */ - oneway void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly); - /** * Called by a client to report that it ran out of graphics memory. */ @@ -304,7 +294,7 @@ interface IWindowSession { * an input channel where the client can receive input. */ void grantInputChannel(int displayId, in SurfaceControl surface, in IWindow window, - in IBinder hostInputToken, int flags, int privateFlags, int type, + in IBinder hostInputToken, int flags, int privateFlags, int inputFeatures, int type, in IBinder windowToken, in IBinder focusGrantToken, String inputHandleName, out InputChannel outInputChannel); @@ -312,7 +302,8 @@ interface IWindowSession { * Update the flags on an input channel associated with a particular surface. */ oneway void updateInputChannel(in IBinder channelToken, int displayId, - in SurfaceControl surface, int flags, int privateFlags, in Region region); + in SurfaceControl surface, int flags, int privateFlags, int inputFeatures, + in Region region); /** * Transfer window focus to an embedded window if the calling window has focus. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 35ed88fc420e..cda1f3adb9a4 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -863,10 +863,8 @@ public interface WindowManager extends ViewManager { * android:value="true|false"/> * </application> * </pre> - * - * @hide */ - // TODO(b/263984287): Make this public API. + // TODO(b/263984287): Add CTS tests. String PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION = "android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION"; @@ -899,8 +897,6 @@ public interface WindowManager extends ViewManager { * android:value="false"/> * </application> * </pre> - * - * @hide */ // TODO(b/263984287): Make this public API. String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS = @@ -937,10 +933,8 @@ public interface WindowManager extends ViewManager { * android:value="true|false"/> * </application> * </pre> - * - * @hide */ - // TODO(b/263984287): Make this public API. + // TODO(b/263984287): Add CTS tests. String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS"; /** @@ -976,10 +970,8 @@ public interface WindowManager extends ViewManager { * android:value="true|false"/> * </application> * </pre> - * - * @hide */ - // TODO(b/263984287): Make this public API. + // TODO(b/263984287): Add CTS tests. String PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION"; @@ -1023,10 +1015,8 @@ public interface WindowManager extends ViewManager { * android:value="true|false"/> * </application> * </pre> - * - * @hide */ - // TODO(b/263984287): Make this public API. + // TODO(b/263984287): Add CTS tests. String PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH"; @@ -1073,17 +1063,28 @@ public interface WindowManager extends ViewManager { * android:value="true|false"/> * </application> * </pre> - * - * @hide */ - // TODO(b/263984287): Make this public API. + // TODO(b/263984287): Add CTS tests. String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE"; /** * Application level {@link android.content.pm.PackageManager.Property PackageManager * .Property} for an app to inform the system that the app should be excluded from the - * compatibility override for orientation set by the device manufacturer. + * compatibility override for orientation set by the device manufacturer. When the orientation + * override is applied it can: + * <ul> + * <li>Replace the specific orientation requested by the app with another selected by the + device manufacturer, e.g. replace undefined requested by the app with portrait. + * <li>Always use an orientation selected by the device manufacturer. + * <li>Do one of the above but only when camera connection is open. + * </ul> + * + * <p>This property is different from {@link PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION} + * (which is used to avoid orientation loops caused by the incorrect use of {@link + * android.app.Activity#setRequestedOrientation}) because this property overrides the app to an + * orientation selected by the device manufacturer rather than ignoring one of orientation + * requests coming from the app while respecting the previous one. * * <p>With this property set to {@code true} or unset, device manufacturers can override * orientation for the app using their discretion to improve display compatibility. @@ -1099,10 +1100,8 @@ public interface WindowManager extends ViewManager { * android:value="true|false"/> * </application> * </pre> - * - * @hide */ - // TODO(b/263984287): Make this public API. + // TODO(b/263984287): Add CTS tests. String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE"; @@ -1144,10 +1143,8 @@ public interface WindowManager extends ViewManager { * android:value="true|false"/> * </application> * </pre> - * - * @hide */ - // TODO(b/263984287): Make this public API. + // TODO(b/263984287): Add CTS tests. String PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE"; @@ -2815,7 +2812,7 @@ public interface WindowManager extends ViewManager { * * @hide */ - public static final int PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED = 0x00000002; + public static final int PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED = 1 << 1; /** * By default, wallpapers are sent new offsets when the wallpaper is scrolled. Wallpapers @@ -2826,7 +2823,7 @@ public interface WindowManager extends ViewManager { * * @hide */ - public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 0x00000004; + public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 1 << 2; /** * When set {@link LayoutParams#TYPE_APPLICATION_OVERLAY} windows will stay visible, even if @@ -2835,7 +2832,7 @@ public interface WindowManager extends ViewManager { * @hide */ @RequiresPermission(permission.SYSTEM_APPLICATION_OVERLAY) - public static final int PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY = 0x00000008; + public static final int PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY = 1 << 3; /** In a multiuser system if this flag is set and the owner is a system process then this * window will appear on all user screens. This overrides the default behavior of window @@ -2845,7 +2842,7 @@ public interface WindowManager extends ViewManager { * {@hide} */ @SystemApi @RequiresPermission(permission.INTERNAL_SYSTEM_WINDOW) - public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 0x00000010; + public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 1 << 4; /** * Flag to allow this window to have unrestricted gesture exclusion. @@ -2853,7 +2850,7 @@ public interface WindowManager extends ViewManager { * @see View#setSystemGestureExclusionRects(List) * @hide */ - public static final int PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION = 0x00000020; + public static final int PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION = 1 << 5; /** * Never animate position changes of the window. @@ -2862,20 +2859,20 @@ public interface WindowManager extends ViewManager { * {@hide} */ @UnsupportedAppUsage - public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 0x00000040; + public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 1 << 6; /** Window flag: special flag to limit the size of the window to be * original size ([320x480] x density). Used to create window for applications * running under compatibility mode. * * {@hide} */ - public static final int PRIVATE_FLAG_COMPATIBLE_WINDOW = 0x00000080; + public static final int PRIVATE_FLAG_COMPATIBLE_WINDOW = 1 << 7; /** Window flag: a special option intended for system dialogs. When * this flag is set, the window will demand focus unconditionally when * it is created. * {@hide} */ - public static final int PRIVATE_FLAG_SYSTEM_ERROR = 0x00000100; + public static final int PRIVATE_FLAG_SYSTEM_ERROR = 1 << 8; /** * Flag to indicate that the view hierarchy of the window can only be measured when @@ -2884,14 +2881,14 @@ public interface WindowManager extends ViewManager { * views. This reduces the chances to perform measure. * {@hide} */ - public static final int PRIVATE_FLAG_OPTIMIZE_MEASURE = 0x00000200; + public static final int PRIVATE_FLAG_OPTIMIZE_MEASURE = 1 << 9; /** * Flag that prevents the wallpaper behind the current window from receiving touch events. * * {@hide} */ - public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 0x00000800; + public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 1 << 10; /** * Flag to force the status bar window to be visible all the time. If the bar is hidden when @@ -2900,7 +2897,7 @@ public interface WindowManager extends ViewManager { * * {@hide} */ - public static final int PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR = 0x00001000; + public static final int PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR = 1 << 11; /** * Flag to indicate that the window frame should be the requested frame adding the display @@ -2910,7 +2907,7 @@ public interface WindowManager extends ViewManager { * * {@hide} */ - public static final int PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT = 0x00002000; + public static final int PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT = 1 << 12; /** * Flag that will make window ignore app visibility and instead depend purely on the decor @@ -2918,39 +2915,28 @@ public interface WindowManager extends ViewManager { * drawing after it launches an app. * @hide */ - public static final int PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY = 0x00004000; - - /** - * Flag to indicate that this window is not expected to be replaced across - * configuration change triggered activity relaunches. In general the WindowManager - * expects Windows to be replaced after relaunch, and thus it will preserve their surfaces - * until the replacement is ready to show in order to prevent visual glitch. However - * some windows, such as PopupWindows expect to be cleared across configuration change, - * and thus should hint to the WindowManager that it should not wait for a replacement. - * @hide - */ - public static final int PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH = 0x00008000; + public static final int PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY = 1 << 13; /** * Flag to indicate that this child window should always be laid-out in the parent * frame regardless of the current windowing mode configuration. * @hide */ - public static final int PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME = 0x00010000; + public static final int PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME = 1 << 14; /** * Flag to indicate that this window is always drawing the status bar background, no matter * what the other flags are. * @hide */ - public static final int PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS = 0x00020000; + public static final int PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS = 1 << 15; /** * Flag to indicate that this window needs Sustained Performance Mode if * the device supports it. * @hide */ - public static final int PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE = 0x00040000; + public static final int PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE = 1 << 16; /** * Flag to indicate that any window added by an application process that is of type @@ -2961,7 +2947,7 @@ public interface WindowManager extends ViewManager { */ @SystemApi @RequiresPermission(permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) - public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 0x00080000; + public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 1 << 19; /** * Indicates that this window is the rounded corners overlay present on some @@ -2969,7 +2955,7 @@ public interface WindowManager extends ViewManager { * screen magnification, and mirroring. * @hide */ - public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000; + public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 1 << 20; /** * Flag to indicate that this window will be excluded while computing the magnifiable region @@ -2983,7 +2969,7 @@ public interface WindowManager extends ViewManager { * </p><p> * @hide */ - public static final int PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION = 0x00200000; + public static final int PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION = 1 << 21; /** * Flag to prevent the window from being magnified by the accessibility magnifier. @@ -2991,7 +2977,7 @@ public interface WindowManager extends ViewManager { * TODO(b/190623172): This is a temporary solution and need to find out another way instead. * @hide */ - public static final int PRIVATE_FLAG_NOT_MAGNIFIABLE = 0x00400000; + public static final int PRIVATE_FLAG_NOT_MAGNIFIABLE = 1 << 22; /** * Flag to indicate that the status bar window is in a state such that it forces showing @@ -3000,54 +2986,54 @@ public interface WindowManager extends ViewManager { * It only takes effects if this is set by {@link LayoutParams#TYPE_STATUS_BAR}. * @hide */ - public static final int PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION = 0x00800000; + public static final int PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION = 1 << 23; /** * Flag to indicate that the window is color space agnostic, and the color can be * interpreted to any color space. * @hide */ - public static final int PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC = 0x01000000; + public static final int PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC = 1 << 24; /** * Flag to request creation of a BLAST (Buffer as LayerState) Layer. * If not specified the client will receive a BufferQueue layer. * @hide */ - public static final int PRIVATE_FLAG_USE_BLAST = 0x02000000; + public static final int PRIVATE_FLAG_USE_BLAST = 1 << 25; /** * Flag to indicate that the window is controlling the appearance of system bars. So we * don't need to adjust it by reading its system UI flags for compatibility. * @hide */ - public static final int PRIVATE_FLAG_APPEARANCE_CONTROLLED = 0x04000000; + public static final int PRIVATE_FLAG_APPEARANCE_CONTROLLED = 1 << 26; /** * Flag to indicate that the window is controlling the behavior of system bars. So we don't * need to adjust it by reading its window flags or system UI flags for compatibility. * @hide */ - public static final int PRIVATE_FLAG_BEHAVIOR_CONTROLLED = 0x08000000; + public static final int PRIVATE_FLAG_BEHAVIOR_CONTROLLED = 1 << 27; /** * Flag to indicate that the window is controlling how it fits window insets on its own. * So we don't need to adjust its attributes for fitting window insets. * @hide */ - public static final int PRIVATE_FLAG_FIT_INSETS_CONTROLLED = 0x10000000; + public static final int PRIVATE_FLAG_FIT_INSETS_CONTROLLED = 1 << 28; /** * Flag to indicate that the window is a trusted overlay. * @hide */ - public static final int PRIVATE_FLAG_TRUSTED_OVERLAY = 0x20000000; + public static final int PRIVATE_FLAG_TRUSTED_OVERLAY = 1 << 29; /** * Flag to indicate that the parent frame of a window should be inset by IME. * @hide */ - public static final int PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME = 0x40000000; + public static final int PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME = 1 << 30; /** * Flag to indicate that we want to intercept and handle global drag and drop for all users. @@ -3062,7 +3048,7 @@ public interface WindowManager extends ViewManager { * @hide */ @RequiresPermission(permission.MANAGE_ACTIVITY_TASKS) - public static final int PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP = 0x80000000; + public static final int PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP = 1 << 31; /** * An internal annotation for flags that can be specified to {@link #softInputMode}. @@ -3093,7 +3079,6 @@ public interface WindowManager extends ViewManager { PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR, PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT, PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY, - PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH, PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME, PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS, PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE, @@ -3169,10 +3154,6 @@ public interface WindowManager extends ViewManager { equals = PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY, name = "FORCE_DECOR_VIEW_VISIBILITY"), @ViewDebug.FlagToString( - mask = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH, - equals = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH, - name = "WILL_NOT_REPLACE_ON_RELAUNCH"), - @ViewDebug.FlagToString( mask = PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME, equals = PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME, name = "LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME"), @@ -3635,9 +3616,20 @@ public interface WindowManager extends ViewManager { /** * The preferred refresh rate for the window. * <p> - * This must be one of the supported refresh rates obtained for the display(s) the window - * is on. The selected refresh rate will be applied to the display's default mode. + * Before API 34, this must be one of the supported refresh rates obtained + * for the display(s) the window is on. The selected refresh rate will be + * applied to the display's default mode. + * <p> + * Starting API 34, this value is not limited to the supported refresh rates + * obtained from the display(s) for the window: it can be any refresh rate + * the window intends to run at. Any refresh rate can be provided as the + * preferred window refresh rate. The OS will select the refresh rate that + * best matches the {@link #preferredRefreshRate}. * <p> + * Setting this value is the equivalent of calling {@link Surface#setFrameRate} with ( + * preferred_frame_rate, + * {@link Surface#FRAME_RATE_COMPATIBILITY_DEFAULT}, + * {@link Surface#CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS}). * This should be used in favor of {@link LayoutParams#preferredDisplayModeId} for * applications that want to specify the refresh rate, but do not want to specify a * preference for any other displayMode properties (e.g., resolution). diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index b157ea0c641f..0560cafe3e52 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -139,7 +139,7 @@ public class WindowlessWindowManager implements IWindowSession { try { mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, state.mSurfaceControl, state.mParams.flags, state.mParams.privateFlags, - state.mInputRegion); + state.mParams.inputFeatures, state.mInputRegion); } catch (RemoteException e) { Log.e(TAG, "Failed to update surface input channel: ", e); } @@ -189,12 +189,13 @@ public class WindowlessWindowManager implements IWindowSession { mRealWm.grantInputChannel(displayId, new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"), window, mHostInputToken, - attrs.flags, attrs.privateFlags, attrs.type, attrs.token, - mFocusGrantToken, attrs.getTitle().toString(), outInputChannel); + attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type, + attrs.token, mFocusGrantToken, attrs.getTitle().toString(), + outInputChannel); } else { mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags, - attrs.privateFlags, attrs.type, attrs.token, mFocusGrantToken, - attrs.getTitle().toString(), outInputChannel); + attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token, + mFocusGrantToken, attrs.getTitle().toString(), outInputChannel); } } catch (RemoteException e) { Log.e(TAG, "Failed to grant input to surface: ", e); @@ -381,16 +382,19 @@ public class WindowlessWindowManager implements IWindowSession { outMergedConfiguration.setConfiguration(mConfiguration, mConfiguration); } - if ((attrChanges & WindowManager.LayoutParams.FLAGS_CHANGED) != 0 - && state.mInputChannelToken != null) { + final int inputChangeMask = WindowManager.LayoutParams.FLAGS_CHANGED + | WindowManager.LayoutParams.INPUT_FEATURES_CHANGED; + if ((attrChanges & inputChangeMask) != 0 && state.mInputChannelToken != null) { try { - if(mRealWm instanceof IWindowSession.Stub) { + if (mRealWm instanceof IWindowSession.Stub) { mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, new SurfaceControl(sc, "WindowlessWindowManager.relayout"), - attrs.flags, attrs.privateFlags, state.mInputRegion); + attrs.flags, attrs.privateFlags, attrs.inputFeatures, + state.mInputRegion); } else { mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc, - attrs.flags, attrs.privateFlags, state.mInputRegion); + attrs.flags, attrs.privateFlags, attrs.inputFeatures, + state.mInputRegion); } } catch (RemoteException e) { Log.e(TAG, "Failed to update surface input channel: ", e); @@ -415,10 +419,6 @@ public class WindowlessWindowManager implements IWindowSession { } @Override - public void prepareToReplaceWindows(android.os.IBinder appToken, boolean childrenOnly) { - } - - @Override public boolean outOfMemory(android.view.IWindow window) { return false; } @@ -564,14 +564,14 @@ public class WindowlessWindowManager implements IWindowSession { @Override public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window, - IBinder hostInputToken, int flags, int privateFlags, int type, + IBinder hostInputToken, int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken, IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) { } @Override public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags, int privateFlags, Region region) { + int flags, int privateFlags, int inputFeatures, Region region) { } @Override diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index c1ec168af145..d54addbbcb8d 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -19,7 +19,6 @@ package android.widget; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1644,8 +1643,7 @@ public class PopupWindow { p.width = mLastWidth = mWidth; } - p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH - | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; + p.privateFlags = PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; // Used for debugging. p.setTitle("PopupWindow:" + Integer.toHexString(hashCode())); diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index ca57c84a1631..fceee4e01799 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -189,6 +189,9 @@ public class Toast { /** * Show the view for the specified duration. + * + * <p>Note that toasts being sent from the background are rate limited, so avoid sending such + * toasts in quick succession. */ public void show() { if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 70a1354e3046..06e91c3bebfa 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -824,6 +824,9 @@ <protected-broadcast android:name="android.companion.virtual.action.VIRTUAL_DEVICE_REMOVED" /> <protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_START_PREVIEW" /> <protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_STOP_PREVIEW" /> + <protected-broadcast android:name="android.app.admin.action.DEVICE_FINANCING_STATE_CHANGED" /> + <protected-broadcast android:name="android.app.admin.action.DEVICE_POLICY_SET_RESULT" /> + <protected-broadcast android:name="android.app.admin.action.DEVICE_POLICY_CHANGED" /> <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 2ed181771cb7..220a1935f51a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -720,14 +720,13 @@ display is powered on at the same time. --> <bool name="config_supportsConcurrentInternalDisplays">true</bool> - <!-- Map of DeviceState to rotation lock setting. Each entry must be in the format - "key:value", for example: "0:1". - The keys are device states, and the values are one of - Settings.Secure.DeviceStateRotationLockSetting. - Any device state that doesn't have a default set here will be treated as - DEVICE_STATE_ROTATION_LOCK_IGNORED meaning it will not have its own rotation lock setting. - If this map is missing, the feature is disabled and only one global rotation lock setting - will apply, regardless of device state. --> + <!-- Map of device posture to rotation lock setting. Each entry must be in the format + "key:value", or "key:value:fallback_key" for example: "0:1" or "2:0:1". The keys are one of + Settings.Secure.DeviceStateRotationLockKey, and the values are one of + Settings.Secure.DeviceStateRotationLockSetting. + The fallback is a key to a device posture that can be specified when the value is + Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED. + --> <string-array name="config_perDeviceStateRotationLockDefaults" /> <!-- Dock behavior --> diff --git a/core/tests/coretests/src/android/view/inputmethod/DeleteRangeGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/DeleteRangeGestureTest.java new file mode 100644 index 000000000000..d7b911dda672 --- /dev/null +++ b/core/tests/coretests/src/android/view/inputmethod/DeleteRangeGestureTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.graphics.RectF; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.compatibility.common.util.ApiTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +@ApiTest(apis = {"android.view.inputmethod.DeleteRangeGesture.Builder#setGranularity", + "android.view.inputmethod.DeleteRangeGesture.Builder#setDeletionStartArea", + "android.view.inputmethod.DeleteRangeGesture.Builder#setDeletionEndArea", + "android.view.inputmethod.DeleteRangeGesture.Builder#setFallbackText", + "android.view.inputmethod.DeleteRangeGesture.Builder#build"}) +public class DeleteRangeGestureTest { + private static final RectF DELETION_START_RECTANGLE = new RectF(1, 2, 3, 4); + private static final RectF DELETION_END_RECTANGLE = new RectF(0, 2, 3, 4); + private static final String FALLBACK_TEXT = "fallback_test"; + + @Test + public void testBuilder() { + DeleteRangeGesture.Builder builder = new DeleteRangeGesture.Builder(); + DeleteRangeGesture gesture = builder.setGranularity(HandwritingGesture.GRANULARITY_WORD) + .setDeletionStartArea(DELETION_START_RECTANGLE) + .setDeletionEndArea(DELETION_END_RECTANGLE) + .setFallbackText(FALLBACK_TEXT).build(); + assertNotNull(gesture); + assertEquals(HandwritingGesture.GRANULARITY_WORD, gesture.getGranularity()); + assertEquals(DELETION_START_RECTANGLE, gesture.getDeletionStartArea()); + assertEquals(DELETION_END_RECTANGLE, gesture.getDeletionEndArea()); + assertEquals(FALLBACK_TEXT, gesture.getFallbackText()); + } +} diff --git a/core/tests/coretests/src/android/view/inputmethod/InsertGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/InsertGestureTest.java new file mode 100644 index 000000000000..47a724d36038 --- /dev/null +++ b/core/tests/coretests/src/android/view/inputmethod/InsertGestureTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.graphics.PointF; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.compatibility.common.util.ApiTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +@ApiTest(apis = {"android.view.inputmethod.InsertGesture.Builder#setInsertionPoint", + "android.view.inputmethod.InsertGesture.Builder#setTextToInsert", + "android.view.inputmethod.InsertGesture.Builder#setFallbackText", + "android.view.inputmethod.InsertGesture.Builder#build"}) +public class InsertGestureTest { + private static final PointF INSERTION_POINT = new PointF(1, 2); + private static final String FALLBACK_TEXT = "fallback_text"; + private static final String TEXT_TO_INSERT = "text"; + + @Test + public void testBuilder() { + InsertGesture.Builder builder = new InsertGesture.Builder(); + InsertGesture gesture = builder.setInsertionPoint(INSERTION_POINT) + .setTextToInsert(TEXT_TO_INSERT) + .setFallbackText(FALLBACK_TEXT).build(); + assertNotNull(gesture); + assertEquals(INSERTION_POINT, gesture.getInsertionPoint()); + assertEquals(FALLBACK_TEXT, gesture.getFallbackText()); + assertEquals(TEXT_TO_INSERT, gesture.getTextToInsert()); + } +} diff --git a/core/tests/coretests/src/android/view/inputmethod/InsertModeGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/InsertModeGestureTest.java new file mode 100644 index 000000000000..11ddba110f7a --- /dev/null +++ b/core/tests/coretests/src/android/view/inputmethod/InsertModeGestureTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.graphics.PointF; +import android.os.CancellationSignal; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.compatibility.common.util.ApiTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +@ApiTest(apis = {"android.view.inputmethod.InsertModeGesture.Builder#setInsertionPoint", + "android.view.inputmethod.InsertModeGesture.Builder#setCancellationSignal", + "android.view.inputmethod.InsertModeGesture.Builder#setFallbackText", + "android.view.inputmethod.InsertModeGesture.Builder#build"}) +public class InsertModeGestureTest { + private static final PointF INSERTION_POINT = new PointF(1, 2); + private static final String FALLBACK_TEXT = "fallback_text"; + private static final CancellationSignal CANCELLATION_SIGNAL = new CancellationSignal(); + + @Test + public void testBuilder() { + InsertModeGesture.Builder builder = new InsertModeGesture.Builder(); + InsertModeGesture gesture = builder.setInsertionPoint(INSERTION_POINT) + .setCancellationSignal(CANCELLATION_SIGNAL) + .setFallbackText(FALLBACK_TEXT).build(); + assertNotNull(gesture); + assertEquals(INSERTION_POINT, gesture.getInsertionPoint()); + assertEquals(FALLBACK_TEXT, gesture.getFallbackText()); + assertEquals(CANCELLATION_SIGNAL, gesture.getCancellationSignal()); + } +} diff --git a/core/tests/coretests/src/android/view/inputmethod/SelectGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/SelectGestureTest.java new file mode 100644 index 000000000000..b2eb07c0a9e7 --- /dev/null +++ b/core/tests/coretests/src/android/view/inputmethod/SelectGestureTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.graphics.RectF; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.compatibility.common.util.ApiTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +@ApiTest(apis = {"android.view.inputmethod.SelectGesture.Builder#setGranularity", + "android.view.inputmethod.SelectGesture.Builder#setSelectionArea", + "android.view.inputmethod.SelectGesture.Builder#setFallbackText", + "android.view.inputmethod.SelectGesture.Builder#build"}) +public class SelectGestureTest { + private static final RectF SELECTION_RECTANGLE = new RectF(1, 2, 3, 4); + private static final String FALLBACK_TEXT = "fallback_text"; + + @Test + public void testBuilder() { + SelectGesture.Builder builder = new SelectGesture.Builder(); + SelectGesture gesture = builder.setGranularity(HandwritingGesture.GRANULARITY_WORD) + .setSelectionArea(SELECTION_RECTANGLE) + .setFallbackText(FALLBACK_TEXT).build(); + assertNotNull(gesture); + assertEquals(HandwritingGesture.GRANULARITY_WORD, gesture.getGranularity()); + assertEquals(SELECTION_RECTANGLE, gesture.getSelectionArea()); + assertEquals(FALLBACK_TEXT, gesture.getFallbackText()); + } +} diff --git a/core/tests/coretests/src/android/view/inputmethod/SelectRangeGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/SelectRangeGestureTest.java new file mode 100644 index 000000000000..df63a4aaaefe --- /dev/null +++ b/core/tests/coretests/src/android/view/inputmethod/SelectRangeGestureTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.graphics.RectF; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.compatibility.common.util.ApiTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +@ApiTest(apis = {"android.view.inputmethod.SelectRangeGesture.Builder#setGranularity", + "android.view.inputmethod.SelectRangeGesture.Builder#setSelectionStartArea", + "android.view.inputmethod.SelectRangeGesture.Builder#setSelectionEndArea", + "android.view.inputmethod.SelectRangeGesture.Builder#setFallbackText", + "android.view.inputmethod.SelectRangeGesture.Builder#build"}) +public class SelectRangeGestureTest { + private static final RectF SELECTION_START_RECTANGLE = new RectF(1, 2, 3, 4); + private static final RectF SELECTION_END_RECTANGLE = new RectF(0, 2, 3, 4); + private static final String FALLBACK_TEXT = "fallback_text"; + + @Test + public void testBuilder() { + SelectRangeGesture.Builder builder = new SelectRangeGesture.Builder(); + SelectRangeGesture gesture = builder.setGranularity(HandwritingGesture.GRANULARITY_WORD) + .setSelectionStartArea(SELECTION_START_RECTANGLE) + .setSelectionEndArea(SELECTION_END_RECTANGLE) + .setFallbackText(FALLBACK_TEXT).build(); + assertNotNull(gesture); + assertEquals(HandwritingGesture.GRANULARITY_WORD, gesture.getGranularity()); + assertEquals(SELECTION_START_RECTANGLE, gesture.getSelectionStartArea()); + assertEquals(SELECTION_END_RECTANGLE, gesture.getSelectionEndArea()); + assertEquals(FALLBACK_TEXT, gesture.getFallbackText()); + } +} diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index ffc5ff226607..5549f88b65e0 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -259,12 +259,6 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-1878839956": { - "message": "Marking app token %s with replacing windows.", - "level": "DEBUG", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "-1872288685": { "message": "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b Callers=%s", "level": "VERBOSE", @@ -463,12 +457,6 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/Task.java" }, - "-1698815688": { - "message": "Resetting app token %s of replacing window marks.", - "level": "DEBUG", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "-1679411993": { "message": "setVr2dDisplayId called for: %d", "level": "DEBUG", @@ -481,12 +469,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-1661704580": { - "message": "Attempted to set replacing window on non-existing app token %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "-1647332198": { "message": "remove RecentTask %s when finishing user %d", "level": "INFO", @@ -613,12 +595,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-1515151503": { - "message": ">>> OPEN TRANSACTION removeReplacedWindows", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, "-1501564055": { "message": "Organized TaskFragment is not ready= %s", "level": "VERBOSE", @@ -691,12 +667,6 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, - "-1471946192": { - "message": "Marking app token %s with replacing child windows.", - "level": "DEBUG", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "-1471518109": { "message": "Set animatingExit: reason=onAppVisibilityChanged win=%s", "level": "DEBUG", @@ -919,12 +889,6 @@ "group": "WM_DEBUG_BACK_PREVIEW", "at": "com\/android\/server\/wm\/BackNavigationController.java" }, - "-1270731689": { - "message": "Attempted to set replacing window on app token with no content %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "-1263316010": { "message": "Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and oldRotation=%s (%d)", "level": "VERBOSE", @@ -1417,12 +1381,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" }, - "-799003045": { - "message": "Set animatingExit: reason=remove\/replaceWindow win=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, "-787664727": { "message": "Cannot launch dream activity due to invalid state. dream component: %s packageName: %s", "level": "ERROR", @@ -1453,6 +1411,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, + "-778347463": { + "message": "Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b mDisplayFrozen=%b callers=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_APP_TRANSITIONS", + "at": "com\/android\/server\/wm\/WindowState.java" + }, "-775004869": { "message": "Not a match: %s", "level": "DEBUG", @@ -1963,12 +1927,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-320419645": { - "message": "Removing replaced window: %s", - "level": "DEBUG", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, "-319689203": { "message": "Reparenting to original parent: %s for %s", "level": "INFO", @@ -2335,12 +2293,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "38267433": { - "message": "Attempted to reset replacing window on non-existing app token %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "45285419": { "message": "startingWindow was set but startingSurface==null, couldn't remove", "level": "VERBOSE", @@ -2989,12 +2941,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "594260654": { - "message": "Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b mWillReplaceWindow=%b mDisplayFrozen=%b callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/WindowState.java" - }, "600140673": { "message": "checkBootAnimationComplete: Waiting for anim complete", "level": "INFO", @@ -3829,12 +3775,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "1423592961": { - "message": "<<< CLOSE TRANSACTION removeReplacedWindows", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, "1430336882": { "message": "findFocusedWindow: focusedApp windows not focusable using new focus @ %s", "level": "VERBOSE", @@ -3901,12 +3841,6 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/WindowState.java" }, - "1515161239": { - "message": "removeDeadWindows: %s", - "level": "WARN", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "1518495446": { "message": "removeWindowToken: Attempted to remove non-existing token: %s", "level": "WARN", @@ -4273,12 +4207,6 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, - "1921821199": { - "message": "Preserving %s until the new one is added", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, "1928325128": { "message": "Run showImeRunner", "level": "DEBUG", @@ -4489,12 +4417,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, - "2114149926": { - "message": "Not removing %s because app died while it's visible", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, "2117696413": { "message": "moveTaskToFront: moving taskId=%d", "level": "DEBUG", diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml index 298ad3025b00..8d1da0f7ad1b 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml @@ -63,11 +63,11 @@ android:tint="@color/bubbles_icon_tint"/> <TextView + android:id="@+id/bubble_manage_menu_dont_bubble_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault" - android:text="@string/bubbles_dont_bubble_conversation" /> + android:textAppearance="@*android:style/TextAppearance.DeviceDefault" /> </LinearLayout> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 3082962e1a8b..9f6cf79dcbc4 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -146,6 +146,8 @@ <string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string> <!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=30] --> <string name="bubble_dismiss_text">Dismiss bubble</string> + <!-- Button text to stop an app from bubbling [CHAR LIMIT=60]--> + <string name="bubbles_dont_bubble">Don\u2019t bubble</string> <!-- Button text to stop a conversation from bubbling [CHAR LIMIT=60]--> <string name="bubbles_dont_bubble_conversation">Don\u2019t bubble conversation</string> <!-- Title text for the bubbles feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]--> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index e5a4362e5bf0..9ccd6ebc51e2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -229,6 +229,7 @@ public class BubbleExpandedView extends LinearLayout { options.setLaunchedFromBubble(true); options.setPendingIntentBackgroundActivityStartMode( MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true); Intent fillInIntent = new Intent(); // Apply flags to make behaviour match documentLaunchMode=always. 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 0b947c8b9b08..deb4fd5f19bb 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 @@ -844,6 +844,8 @@ public class BubbleStackView extends FrameLayout private DismissView mDismissView; private ViewGroup mManageMenu; + private TextView mManageDontBubbleText; + private ViewGroup mManageSettingsView; private ImageView mManageSettingsIcon; private TextView mManageSettingsText; private boolean mShowingManage = false; @@ -1217,7 +1219,11 @@ public class BubbleStackView extends FrameLayout mUnbubbleConversationCallback.accept(mBubbleData.getSelectedBubble().getKey()); }); - mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container).setOnClickListener( + mManageDontBubbleText = mManageMenu + .findViewById(R.id.bubble_manage_menu_dont_bubble_text); + + mManageSettingsView = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container); + mManageSettingsView.setOnClickListener( view -> { showManageMenu(false /* show */); final BubbleViewProvider bubble = mBubbleData.getSelectedBubble(); @@ -2868,10 +2874,19 @@ public class BubbleStackView extends FrameLayout // name and icon. if (show) { final Bubble bubble = mBubbleData.getBubbleInStackWithKey(mExpandedBubble.getKey()); - if (bubble != null) { + if (bubble != null && !bubble.isAppBubble()) { + // Setup options for non app bubbles + mManageDontBubbleText.setText(R.string.bubbles_dont_bubble_conversation); mManageSettingsIcon.setImageBitmap(bubble.getRawAppBadge()); mManageSettingsText.setText(getResources().getString( R.string.bubbles_app_settings, bubble.getAppName())); + mManageSettingsView.setVisibility(VISIBLE); + } else { + // Setup options for app bubbles + mManageDontBubbleText.setText(R.string.bubbles_dont_bubble); + // App bubbles are not notification based + // so we don't show the option to go to notification settings + mManageSettingsView.setVisibility(GONE); } } @@ -2936,6 +2951,15 @@ public class BubbleStackView extends FrameLayout } } + /** + * Checks whether manage menu notification settings action is available and visible + * Used for testing + */ + @VisibleForTesting + public boolean isManageMenuSettingsVisible() { + return mManageSettingsView != null && mManageSettingsView.getVisibility() == VISIBLE; + } + private void updateExpandedBubble() { if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "updateExpandedBubble()"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index 0a9c3310a883..8cb575cc96e3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -103,6 +103,7 @@ class DragResizeInputListener implements AutoCloseable { null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, + 0 /* inputFeatures */, TYPE_APPLICATION, null /* windowToken */, mFocusGrantToken, @@ -208,6 +209,7 @@ class DragResizeInputListener implements AutoCloseable { mDecorationSurface, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, + 0 /* inputFeatures */, touchRegion); } catch (RemoteException e) { e.rethrowFromSystemServer(); diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp index c3984055a278..c3ad7670d473 100644 --- a/libs/input/MouseCursorController.cpp +++ b/libs/input/MouseCursorController.cpp @@ -53,8 +53,6 @@ MouseCursorController::MouseCursorController(PointerControllerContext& context) mLocked.resolvedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED; mLocked.resourcesLoaded = false; - - mLocked.buttonState = 0; } MouseCursorController::~MouseCursorController() { @@ -95,22 +93,6 @@ void MouseCursorController::move(float deltaX, float deltaY) { setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); } -void MouseCursorController::setButtonState(int32_t buttonState) { -#if DEBUG_MOUSE_CURSOR_UPDATES - ALOGD("Set button state 0x%08x", buttonState); -#endif - std::scoped_lock lock(mLock); - - if (mLocked.buttonState != buttonState) { - mLocked.buttonState = buttonState; - } -} - -int32_t MouseCursorController::getButtonState() const { - std::scoped_lock lock(mLock); - return mLocked.buttonState; -} - void MouseCursorController::setPosition(float x, float y) { #if DEBUG_MOUSE_CURSOR_UPDATES ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h index 26be2a858c4e..00dc0854440e 100644 --- a/libs/input/MouseCursorController.h +++ b/libs/input/MouseCursorController.h @@ -45,8 +45,6 @@ public: std::optional<FloatRect> getBounds() const; void move(float deltaX, float deltaY); - void setButtonState(int32_t buttonState); - int32_t getButtonState() const; void setPosition(float x, float y); FloatPoint getPosition() const; int32_t getDisplayId() const; @@ -96,8 +94,6 @@ private: PointerIconStyle requestedPointerType; PointerIconStyle resolvedPointerType; - int32_t buttonState; - bool animating{false}; } mLocked GUARDED_BY(mLock); diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 544edc2a716f..88e351963148 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -136,14 +136,6 @@ void PointerController::move(float deltaX, float deltaY) { mCursorController.move(transformed.x, transformed.y); } -void PointerController::setButtonState(int32_t buttonState) { - mCursorController.setButtonState(buttonState); -} - -int32_t PointerController::getButtonState() const { - return mCursorController.getButtonState(); -} - void PointerController::setPosition(float x, float y) { const int32_t displayId = mCursorController.getDisplayId(); vec2 transformed; diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 6d3557c89cc7..ca14b6e9bfdc 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -50,21 +50,19 @@ public: ~PointerController() override; - virtual std::optional<FloatRect> getBounds() const; - virtual void move(float deltaX, float deltaY); - virtual void setButtonState(int32_t buttonState); - virtual int32_t getButtonState() const; - virtual void setPosition(float x, float y); - virtual FloatPoint getPosition() const; - virtual int32_t getDisplayId() const; - virtual void fade(Transition transition); - virtual void unfade(Transition transition); - virtual void setDisplayViewport(const DisplayViewport& viewport); - - virtual void setPresentation(Presentation presentation); - virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, - BitSet32 spotIdBits, int32_t displayId); - virtual void clearSpots(); + std::optional<FloatRect> getBounds() const override; + void move(float deltaX, float deltaY) override; + void setPosition(float x, float y) override; + FloatPoint getPosition() const override; + int32_t getDisplayId() const override; + void fade(Transition transition) override; + void unfade(Transition transition) override; + void setDisplayViewport(const DisplayViewport& viewport) override; + + void setPresentation(Presentation presentation) override; + void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId) override; + void clearSpots() override; void updatePointerIcon(PointerIconStyle iconId); void setCustomPointerIcon(const SpriteIcon& icon); diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java index 7946baee200a..f939f523d3fa 100644 --- a/media/java/android/media/tv/ITvInputSessionWrapper.java +++ b/media/java/android/media/tv/ITvInputSessionWrapper.java @@ -280,7 +280,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand } case DO_NOTIFY_TV_MESSAGE: { SomeArgs args = (SomeArgs) msg.obj; - mTvInputSessionImpl.onTvMessageReceived((String) args.arg1, (Bundle) args.arg2); + mTvInputSessionImpl.onTvMessageReceived((Integer) args.arg1, (Bundle) args.arg2); break; } default: { diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 4e380c41ccdb..0d283fa87c19 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -1512,7 +1512,7 @@ public abstract class TvInputService extends Service { * See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on * how to parse this data. */ - public void onTvMessage(@NonNull @TvInputManager.TvMessageType String type, + public void onTvMessage(@TvInputManager.TvMessageType int type, @NonNull Bundle data) { } @@ -2065,7 +2065,7 @@ public abstract class TvInputService extends Service { onAdBufferReady(buffer); } - void onTvMessageReceived(String type, Bundle data) { + void onTvMessageReceived(int type, Bundle data) { onTvMessage(type, data); } diff --git a/packages/SettingsLib/DeviceStateRotationLock/Android.bp b/packages/SettingsLib/DeviceStateRotationLock/Android.bp index c642bd14ed79..103309a43bb8 100644 --- a/packages/SettingsLib/DeviceStateRotationLock/Android.bp +++ b/packages/SettingsLib/DeviceStateRotationLock/Android.bp @@ -10,7 +10,10 @@ package { android_library { name: "SettingsLibDeviceStateRotationLock", - srcs: ["src/**/*.java"], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], min_sdk_version: "21", } diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java index 10b004e1b243..76e1df1459e3 100644 --- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java @@ -57,17 +57,19 @@ public final class DeviceStateRotationLockSettingsManager { private final Handler mMainHandler = new Handler(Looper.getMainLooper()); private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>(); private final SecureSettings mSecureSettings; - private String[] mDeviceStateRotationLockDefaults; - private SparseIntArray mDeviceStateRotationLockSettings; - private SparseIntArray mDeviceStateDefaultRotationLockSettings; - private SparseIntArray mDeviceStateRotationLockFallbackSettings; + private final PosturesHelper mPosturesHelper; + private String[] mPostureRotationLockDefaults; + private SparseIntArray mPostureRotationLockSettings; + private SparseIntArray mPostureDefaultRotationLockSettings; + private SparseIntArray mPostureRotationLockFallbackSettings; private String mLastSettingValue; private List<SettableDeviceState> mSettableDeviceStates; @VisibleForTesting DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) { - this.mSecureSettings = secureSettings; - mDeviceStateRotationLockDefaults = + mSecureSettings = secureSettings; + mPosturesHelper = new PosturesHelper(context); + mPostureRotationLockDefaults = context.getResources() .getStringArray(R.array.config_perDeviceStateRotationLockDefaults); loadDefaults(); @@ -134,13 +136,14 @@ public final class DeviceStateRotationLockSettingsManager { /** Updates the rotation lock setting for a specified device state. */ public void updateSetting(int deviceState, boolean rotationLocked) { - if (mDeviceStateRotationLockFallbackSettings.indexOfKey(deviceState) >= 0) { - // The setting for this device state is IGNORED, and has a fallback device state. - // The setting for that fallback device state should be the changed in this case. - deviceState = mDeviceStateRotationLockFallbackSettings.get(deviceState); + int posture = mPosturesHelper.deviceStateToPosture(deviceState); + if (mPostureRotationLockFallbackSettings.indexOfKey(posture) >= 0) { + // The setting for this device posture is IGNORED, and has a fallback posture. + // The setting for that fallback posture should be the changed in this case. + posture = mPostureRotationLockFallbackSettings.get(posture); } - mDeviceStateRotationLockSettings.put( - deviceState, + mPostureRotationLockSettings.put( + posture, rotationLocked ? DEVICE_STATE_ROTATION_LOCK_LOCKED : DEVICE_STATE_ROTATION_LOCK_UNLOCKED); @@ -159,22 +162,23 @@ public final class DeviceStateRotationLockSettingsManager { */ @Settings.Secure.DeviceStateRotationLockSetting public int getRotationLockSetting(int deviceState) { - int rotationLockSetting = mDeviceStateRotationLockSettings.get( - deviceState, /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED); + int devicePosture = mPosturesHelper.deviceStateToPosture(deviceState); + int rotationLockSetting = mPostureRotationLockSettings.get( + devicePosture, /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED); if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) { - rotationLockSetting = getFallbackRotationLockSetting(deviceState); + rotationLockSetting = getFallbackRotationLockSetting(devicePosture); } return rotationLockSetting; } - private int getFallbackRotationLockSetting(int deviceState) { - int indexOfFallbackState = mDeviceStateRotationLockFallbackSettings.indexOfKey(deviceState); - if (indexOfFallbackState < 0) { + private int getFallbackRotationLockSetting(int devicePosture) { + int indexOfFallback = mPostureRotationLockFallbackSettings.indexOfKey(devicePosture); + if (indexOfFallback < 0) { Log.w(TAG, "Setting is ignored, but no fallback was specified."); return DEVICE_STATE_ROTATION_LOCK_IGNORED; } - int fallbackState = mDeviceStateRotationLockFallbackSettings.valueAt(indexOfFallbackState); - return mDeviceStateRotationLockSettings.get(fallbackState, + int fallbackPosture = mPostureRotationLockFallbackSettings.valueAt(indexOfFallback); + return mPostureRotationLockSettings.get(fallbackPosture, /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED); } @@ -189,8 +193,8 @@ public final class DeviceStateRotationLockSettingsManager { * DEVICE_STATE_ROTATION_LOCK_UNLOCKED}. */ public boolean isRotationLockedForAllStates() { - for (int i = 0; i < mDeviceStateRotationLockSettings.size(); i++) { - if (mDeviceStateRotationLockSettings.valueAt(i) + for (int i = 0; i < mPostureRotationLockSettings.size(); i++) { + if (mPostureRotationLockSettings.valueAt(i) == DEVICE_STATE_ROTATION_LOCK_UNLOCKED) { return false; } @@ -221,7 +225,7 @@ public final class DeviceStateRotationLockSettingsManager { fallbackOnDefaults(); return; } - mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2); + mPostureRotationLockSettings = new SparseIntArray(values.length / 2); int key; int value; @@ -230,7 +234,7 @@ public final class DeviceStateRotationLockSettingsManager { key = Integer.parseInt(values[i++]); value = Integer.parseInt(values[i++]); boolean isPersistedValueIgnored = value == DEVICE_STATE_ROTATION_LOCK_IGNORED; - boolean isDefaultValueIgnored = mDeviceStateDefaultRotationLockSettings.get(key) + boolean isDefaultValueIgnored = mPostureDefaultRotationLockSettings.get(key) == DEVICE_STATE_ROTATION_LOCK_IGNORED; if (isPersistedValueIgnored != isDefaultValueIgnored) { Log.w(TAG, "Conflict for ignored device state " + key @@ -238,7 +242,7 @@ public final class DeviceStateRotationLockSettingsManager { fallbackOnDefaults(); return; } - mDeviceStateRotationLockSettings.put(key, value); + mPostureRotationLockSettings.put(key, value); } catch (NumberFormatException e) { Log.wtf(TAG, "Error deserializing one of the saved settings", e); fallbackOnDefaults(); @@ -253,7 +257,7 @@ public final class DeviceStateRotationLockSettingsManager { */ @VisibleForTesting public void resetStateForTesting(Resources resources) { - mDeviceStateRotationLockDefaults = + mPostureRotationLockDefaults = resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults); fallbackOnDefaults(); } @@ -264,23 +268,23 @@ public final class DeviceStateRotationLockSettingsManager { } private void persistSettings() { - if (mDeviceStateRotationLockSettings.size() == 0) { + if (mPostureRotationLockSettings.size() == 0) { persistSettingIfChanged(/* newSettingValue= */ ""); return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder - .append(mDeviceStateRotationLockSettings.keyAt(0)) + .append(mPostureRotationLockSettings.keyAt(0)) .append(SEPARATOR_REGEX) - .append(mDeviceStateRotationLockSettings.valueAt(0)); + .append(mPostureRotationLockSettings.valueAt(0)); - for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) { + for (int i = 1; i < mPostureRotationLockSettings.size(); i++) { stringBuilder .append(SEPARATOR_REGEX) - .append(mDeviceStateRotationLockSettings.keyAt(i)) + .append(mPostureRotationLockSettings.keyAt(i)) .append(SEPARATOR_REGEX) - .append(mDeviceStateRotationLockSettings.valueAt(i)); + .append(mPostureRotationLockSettings.valueAt(i)); } persistSettingIfChanged(stringBuilder.toString()); } @@ -300,22 +304,20 @@ public final class DeviceStateRotationLockSettingsManager { } private void loadDefaults() { - mSettableDeviceStates = new ArrayList<>(mDeviceStateRotationLockDefaults.length); - mDeviceStateDefaultRotationLockSettings = new SparseIntArray( - mDeviceStateRotationLockDefaults.length); - mDeviceStateRotationLockSettings = new SparseIntArray( - mDeviceStateRotationLockDefaults.length); - mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1); - for (String entry : mDeviceStateRotationLockDefaults) { + mSettableDeviceStates = new ArrayList<>(mPostureRotationLockDefaults.length); + mPostureDefaultRotationLockSettings = new SparseIntArray( + mPostureRotationLockDefaults.length); + mPostureRotationLockSettings = new SparseIntArray(mPostureRotationLockDefaults.length); + mPostureRotationLockFallbackSettings = new SparseIntArray(1); + for (String entry : mPostureRotationLockDefaults) { String[] values = entry.split(SEPARATOR_REGEX); try { - int deviceState = Integer.parseInt(values[0]); + int posture = Integer.parseInt(values[0]); int rotationLockSetting = Integer.parseInt(values[1]); if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) { if (values.length == 3) { - int fallbackDeviceState = Integer.parseInt(values[2]); - mDeviceStateRotationLockFallbackSettings.put(deviceState, - fallbackDeviceState); + int fallbackPosture = Integer.parseInt(values[2]); + mPostureRotationLockFallbackSettings.put(posture, fallbackPosture); } else { Log.w(TAG, "Rotation lock setting is IGNORED, but values have unexpected " @@ -324,9 +326,14 @@ public final class DeviceStateRotationLockSettingsManager { } } boolean isSettable = rotationLockSetting != DEVICE_STATE_ROTATION_LOCK_IGNORED; - mSettableDeviceStates.add(new SettableDeviceState(deviceState, isSettable)); - mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting); - mDeviceStateDefaultRotationLockSettings.put(deviceState, rotationLockSetting); + Integer deviceState = mPosturesHelper.postureToDeviceState(posture); + if (deviceState != null) { + mSettableDeviceStates.add(new SettableDeviceState(deviceState, isSettable)); + } else { + Log.wtf(TAG, "No matching device state for posture: " + posture); + } + mPostureRotationLockSettings.put(posture, rotationLockSetting); + mPostureDefaultRotationLockSettings.put(posture, rotationLockSetting); } catch (NumberFormatException e) { Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e); return; @@ -338,13 +345,11 @@ public final class DeviceStateRotationLockSettingsManager { public void dump(IndentingPrintWriter pw) { pw.println("DeviceStateRotationLockSettingsManager"); pw.increaseIndent(); - pw.println("mDeviceStateRotationLockDefaults: " + Arrays.toString( - mDeviceStateRotationLockDefaults)); - pw.println("mDeviceStateDefaultRotationLockSettings: " - + mDeviceStateDefaultRotationLockSettings); - pw.println("mDeviceStateRotationLockSettings: " + mDeviceStateRotationLockSettings); - pw.println("mDeviceStateRotationLockFallbackSettings: " - + mDeviceStateRotationLockFallbackSettings); + pw.println("mPostureRotationLockDefaults: " + + Arrays.toString(mPostureRotationLockDefaults)); + pw.println("mPostureDefaultRotationLockSettings: " + mPostureDefaultRotationLockSettings); + pw.println("mDeviceStateRotationLockSettings: " + mPostureRotationLockSettings); + pw.println("mPostureRotationLockFallbackSettings: " + mPostureRotationLockFallbackSettings); pw.println("mSettableDeviceStates: " + mSettableDeviceStates); pw.println("mLastSettingValue: " + mLastSettingValue); pw.decreaseIndent(); diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt new file mode 100644 index 000000000000..9c70be9c1f66 --- /dev/null +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt @@ -0,0 +1,55 @@ +/* + * 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.settingslib.devicestate + +import android.content.Context +import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED +import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED +import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED +import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNKNOWN +import android.provider.Settings.Secure.DeviceStateRotationLockKey +import com.android.internal.R + +/** Helps to convert between device state and posture. */ +class PosturesHelper(context: Context) { + + private val foldedDeviceStates = + context.resources.getIntArray(R.array.config_foldedDeviceStates) + private val halfFoldedDeviceStates = + context.resources.getIntArray(R.array.config_halfFoldedDeviceStates) + private val unfoldedDeviceStates = + context.resources.getIntArray(R.array.config_openDeviceStates) + + @DeviceStateRotationLockKey + fun deviceStateToPosture(deviceState: Int): Int { + return when (deviceState) { + in foldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_FOLDED + in halfFoldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_HALF_FOLDED + in unfoldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_UNFOLDED + else -> DEVICE_STATE_ROTATION_KEY_UNKNOWN + } + } + + fun postureToDeviceState(@DeviceStateRotationLockKey posture: Int): Int? { + return when (posture) { + DEVICE_STATE_ROTATION_KEY_FOLDED -> foldedDeviceStates.firstOrNull() + DEVICE_STATE_ROTATION_KEY_HALF_FOLDED -> halfFoldedDeviceStates.firstOrNull() + DEVICE_STATE_ROTATION_KEY_UNFOLDED -> unfoldedDeviceStates.firstOrNull() + else -> null + } + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt index ca88f8da63c9..215f6b964ad3 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt @@ -16,7 +16,6 @@ package com.android.settingslib.spa.framework.common -import android.app.settings.SettingsEnums import android.os.Bundle // Defines the category of the log, for quick filter @@ -32,14 +31,14 @@ enum class LogCategory { } // Defines the log events in Spa. -enum class LogEvent(val action: Int) { +enum class LogEvent { // Page related events. - PAGE_ENTER(SettingsEnums.PAGE_VISIBLE), - PAGE_LEAVE(SettingsEnums.PAGE_HIDE), + PAGE_ENTER, + PAGE_LEAVE, // Entry related events. - ENTRY_CLICK(SettingsEnums.ACTION_SETTINGS_TILE_CLICK), - ENTRY_SWITCH(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE), + ENTRY_CLICK, + ENTRY_SWITCH, } internal const val LOG_DATA_DISPLAY_NAME = "name" diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java index 0fa15eb6bc0c..fdefcde3a170 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java @@ -70,12 +70,20 @@ public class DeviceStateRotationLockSettingsManagerTest { when(mMockContext.getApplicationContext()).thenReturn(mMockContext); when(mMockContext.getResources()).thenReturn(mMockResources); when(mMockContext.getContentResolver()).thenReturn(context.getContentResolver()); + when(mMockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)) + .thenReturn(new String[]{"0:1", "1:0:2", "2:2"}); + when(mMockResources.getIntArray(R.array.config_foldedDeviceStates)) + .thenReturn(new int[]{0}); + when(mMockResources.getIntArray(R.array.config_halfFoldedDeviceStates)) + .thenReturn(new int[]{1}); + when(mMockResources.getIntArray(R.array.config_openDeviceStates)) + .thenReturn(new int[]{2}); mFakeSecureSettings.registerContentObserver( Settings.Secure.DEVICE_STATE_ROTATION_LOCK, /* notifyForDescendents= */ false, //NOTYPO mContentObserver, UserHandle.USER_CURRENT); - mManager = new DeviceStateRotationLockSettingsManager(context, mFakeSecureSettings); + mManager = new DeviceStateRotationLockSettingsManager(mMockContext, mFakeSecureSettings); } @Test @@ -109,7 +117,7 @@ public class DeviceStateRotationLockSettingsManagerTest { public void getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() { when(mMockResources.getStringArray( R.array.config_perDeviceStateRotationLockDefaults)).thenReturn( - new String[]{"2:2", "4:0", "1:1", "0:0"}); + new String[]{"2:1", "1:0:1", "0:2"}); List<SettableDeviceState> settableDeviceStates = DeviceStateRotationLockSettingsManager.getInstance( @@ -117,9 +125,8 @@ public class DeviceStateRotationLockSettingsManagerTest { assertThat(settableDeviceStates).containsExactly( new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true), - new SettableDeviceState(/* deviceState= */ 4, /* isSettable= */ false), - new SettableDeviceState(/* deviceState= */ 1, /* isSettable= */ true), - new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ false) + new SettableDeviceState(/* deviceState= */ 1, /* isSettable= */ false), + new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ true) ).inOrder(); } diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt index 280e7ed9ea2e..23fcb691ddb4 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt @@ -15,163 +15,166 @@ */ package com.android.systemui.surfaceeffects.shaderutil -/** A common utility functions that are used for computing shaders. */ -class ShaderUtilLibrary { +/** Common utility functions that are used for computing shaders. */ +object ShaderUtilLibrary { // language=AGSL - companion object { - const val SHADER_LIB = - """ - float triangleNoise(vec2 n) { - n = fract(n * vec2(5.3987, 5.4421)); - n += dot(n.yx, n.xy + vec2(21.5351, 14.3137)); - float xy = n.x * n.y; - // compute in [0..2[ and remap to [-1.0..1.0[ - return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0; + const val SHADER_LIB = + """ + float triangleNoise(vec2 n) { + n = fract(n * vec2(5.3987, 5.4421)); + n += dot(n.yx, n.xy + vec2(21.5351, 14.3137)); + float xy = n.x * n.y; + // compute in [0..2[ and remap to [-1.0..1.0[ + return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0; + } + + const float PI = 3.1415926535897932384626; + + float sparkles(vec2 uv, float t) { + float n = triangleNoise(uv); + float s = 0.0; + for (float i = 0; i < 4; i += 1) { + float l = i * 0.01; + float h = l + 0.1; + float o = smoothstep(n - l, h, n); + o *= abs(sin(PI * o * (t + 0.55 * i))); + s += o; } - - const float PI = 3.1415926535897932384626; - - float sparkles(vec2 uv, float t) { - float n = triangleNoise(uv); - float s = 0.0; - for (float i = 0; i < 4; i += 1) { - float l = i * 0.01; - float h = l + 0.1; - float o = smoothstep(n - l, h, n); - o *= abs(sin(PI * o * (t + 0.55 * i))); - s += o; - } - return s; - } - - vec2 distort(vec2 p, float time, float distort_amount_radial, - float distort_amount_xy) { - float angle = atan(p.y, p.x); - return p + vec2(sin(angle * 8 + time * 0.003 + 1.641), - cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial - + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123), - cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy; - } - - // Perceived luminosity (L′), not absolute luminosity. - half getLuminosity(vec3 c) { - return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; - } - - // Creates a luminosity mask and clamp to the legal range. - vec3 maskLuminosity(vec3 dest, float lum) { - dest.rgb *= vec3(lum); - // Clip back into the legal range - dest = clamp(dest, vec3(0.), vec3(1.0)); - return dest; - } - - // Return range [-1, 1]. - vec3 hash(vec3 p) { - p = fract(p * vec3(.3456, .1234, .9876)); - p += dot(p, p.yxz + 43.21); - p = (p.xxy + p.yxx) * p.zyx; - return (fract(sin(p) * 4567.1234567) - .5) * 2.; - } - - // Skew factors (non-uniform). - const half SKEW = 0.3333333; // 1/3 - const half UNSKEW = 0.1666667; // 1/6 - - // Return range roughly [-1,1]. - // It's because the hash function (that returns a random gradient vector) returns - // different magnitude of vectors. Noise doesn't have to be in the precise range thus - // skipped normalize. - half simplex3d(vec3 p) { - // Skew the input coordinate, so that we get squashed cubical grid - vec3 s = floor(p + (p.x + p.y + p.z) * SKEW); - - // Unskew back - vec3 u = s - (s.x + s.y + s.z) * UNSKEW; - - // Unskewed coordinate that is relative to p, to compute the noise contribution - // based on the distance. - vec3 c0 = p - u; - - // We have six simplices (in this case tetrahedron, since we are in 3D) that we - // could possibly in. - // Here, we are finding the correct tetrahedron (simplex shape), and traverse its - // four vertices (c0..3) when computing noise contribution. - // The way we find them is by comparing c0's x,y,z values. - // For example in 2D, we can find the triangle (simplex shape in 2D) that we are in - // by comparing x and y values. i.e. x>y lower, x<y, upper triangle. - // Same applies in 3D. - // - // Below indicates the offsets (or offset directions) when c0=(x0,y0,z0) - // x0>y0>z0: (1,0,0), (1,1,0), (1,1,1) - // x0>z0>y0: (1,0,0), (1,0,1), (1,1,1) - // z0>x0>y0: (0,0,1), (1,0,1), (1,1,1) - // z0>y0>x0: (0,0,1), (0,1,1), (1,1,1) - // y0>z0>x0: (0,1,0), (0,1,1), (1,1,1) - // y0>x0>z0: (0,1,0), (1,1,0), (1,1,1) - // - // The rule is: - // * For offset1, set 1 at the max component, otherwise 0. - // * For offset2, set 0 at the min component, otherwise 1. - // * For offset3, set 1 for all. - // - // Encode x0-y0, y0-z0, z0-x0 in a vec3 - vec3 en = c0 - c0.yzx; - // Each represents whether x0>y0, y0>z0, z0>x0 - en = step(vec3(0.), en); - // en.zxy encodes z0>x0, x0>y0, y0>x0 - vec3 offset1 = en * (1. - en.zxy); // find max - vec3 offset2 = 1. - en.zxy * (1. - en); // 1-(find min) - vec3 offset3 = vec3(1.); - - vec3 c1 = c0 - offset1 + UNSKEW; - vec3 c2 = c0 - offset2 + UNSKEW * 2.; - vec3 c3 = c0 - offset3 + UNSKEW * 3.; - - // Kernel summation: dot(max(0, r^2-d^2))^4, noise contribution) - // - // First compute d^2, squared distance to the point. - vec4 w; // w = max(0, r^2 - d^2)) - w.x = dot(c0, c0); - w.y = dot(c1, c1); - w.z = dot(c2, c2); - w.w = dot(c3, c3); - - // Noise contribution should decay to zero before they cross the simplex boundary. - // Usually r^2 is 0.5 or 0.6; - // 0.5 ensures continuity but 0.6 increases the visual quality for the application - // where discontinuity isn't noticeable. - w = max(0.6 - w, 0.); - - // Noise contribution from each point. - vec4 nc; - nc.x = dot(hash(s), c0); - nc.y = dot(hash(s + offset1), c1); - nc.z = dot(hash(s + offset2), c2); - nc.w = dot(hash(s + offset3), c3); - - nc *= w*w*w*w; - - // Add all the noise contributions. - // Should multiply by the possible max contribution to adjust the range in [-1,1]. - return dot(vec4(32.), nc); - } - - // Random rotations. - // The way you create fractal noise is layering simplex noise with some rotation. - // To make random cloud looking noise, the rotations should not align. (Otherwise it - // creates patterned noise). - // Below rotations only rotate in one axis. - const mat3 rot1 = mat3(1.0, 0. ,0., 0., 0.15, -0.98, 0., 0.98, 0.15); - const mat3 rot2 = mat3(-0.95, 0. ,-0.3, 0., 1., 0., 0.3, 0., -0.95); - const mat3 rot3 = mat3(1.0, 0. ,0., 0., -0.44, -0.89, 0., 0.89, -0.44); - - // Octave = 4 - // Divide each coefficient by 3 to produce more grainy noise. - half simplex3d_fractal(vec3 mat) { - return 0.675 * simplex3d(mat * rot1) + 0.225 * simplex3d(2.0 * mat * rot2) - + 0.075 * simplex3d(4.0 * mat * rot3) + 0.025 * simplex3d(8.0 * mat); - } - """ - } + return s; + } + + vec2 distort(vec2 p, float time, float distort_amount_radial, + float distort_amount_xy) { + float angle = atan(p.y, p.x); + return p + vec2(sin(angle * 8 + time * 0.003 + 1.641), + cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial + + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123), + cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy; + } + + // Perceived luminosity (L′), not absolute luminosity. + half getLuminosity(vec3 c) { + return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; + } + + // Creates a luminosity mask and clamp to the legal range. + vec3 maskLuminosity(vec3 dest, float lum) { + dest.rgb *= vec3(lum); + // Clip back into the legal range + dest = clamp(dest, vec3(0.), vec3(1.0)); + return dest; + } + + // Return range [-1, 1]. + vec3 hash(vec3 p) { + p = fract(p * vec3(.3456, .1234, .9876)); + p += dot(p, p.yxz + 43.21); + p = (p.xxy + p.yxx) * p.zyx; + return (fract(sin(p) * 4567.1234567) - .5) * 2.; + } + + // Skew factors (non-uniform). + const half SKEW = 0.3333333; // 1/3 + const half UNSKEW = 0.1666667; // 1/6 + + // Return range roughly [-1,1]. + // It's because the hash function (that returns a random gradient vector) returns + // different magnitude of vectors. Noise doesn't have to be in the precise range thus + // skipped normalize. + half simplex3d(vec3 p) { + // Skew the input coordinate, so that we get squashed cubical grid + vec3 s = floor(p + (p.x + p.y + p.z) * SKEW); + + // Unskew back + vec3 u = s - (s.x + s.y + s.z) * UNSKEW; + + // Unskewed coordinate that is relative to p, to compute the noise contribution + // based on the distance. + vec3 c0 = p - u; + + // We have six simplices (in this case tetrahedron, since we are in 3D) that we + // could possibly in. + // Here, we are finding the correct tetrahedron (simplex shape), and traverse its + // four vertices (c0..3) when computing noise contribution. + // The way we find them is by comparing c0's x,y,z values. + // For example in 2D, we can find the triangle (simplex shape in 2D) that we are in + // by comparing x and y values. i.e. x>y lower, x<y, upper triangle. + // Same applies in 3D. + // + // Below indicates the offsets (or offset directions) when c0=(x0,y0,z0) + // x0>y0>z0: (1,0,0), (1,1,0), (1,1,1) + // x0>z0>y0: (1,0,0), (1,0,1), (1,1,1) + // z0>x0>y0: (0,0,1), (1,0,1), (1,1,1) + // z0>y0>x0: (0,0,1), (0,1,1), (1,1,1) + // y0>z0>x0: (0,1,0), (0,1,1), (1,1,1) + // y0>x0>z0: (0,1,0), (1,1,0), (1,1,1) + // + // The rule is: + // * For offset1, set 1 at the max component, otherwise 0. + // * For offset2, set 0 at the min component, otherwise 1. + // * For offset3, set 1 for all. + // + // Encode x0-y0, y0-z0, z0-x0 in a vec3 + vec3 en = c0 - c0.yzx; + // Each represents whether x0>y0, y0>z0, z0>x0 + en = step(vec3(0.), en); + // en.zxy encodes z0>x0, x0>y0, y0>x0 + vec3 offset1 = en * (1. - en.zxy); // find max + vec3 offset2 = 1. - en.zxy * (1. - en); // 1-(find min) + vec3 offset3 = vec3(1.); + + vec3 c1 = c0 - offset1 + UNSKEW; + vec3 c2 = c0 - offset2 + UNSKEW * 2.; + vec3 c3 = c0 - offset3 + UNSKEW * 3.; + + // Kernel summation: dot(max(0, r^2-d^2))^4, noise contribution) + // + // First compute d^2, squared distance to the point. + vec4 w; // w = max(0, r^2 - d^2)) + w.x = dot(c0, c0); + w.y = dot(c1, c1); + w.z = dot(c2, c2); + w.w = dot(c3, c3); + + // Noise contribution should decay to zero before they cross the simplex boundary. + // Usually r^2 is 0.5 or 0.6; + // 0.5 ensures continuity but 0.6 increases the visual quality for the application + // where discontinuity isn't noticeable. + w = max(0.6 - w, 0.); + + // Noise contribution from each point. + vec4 nc; + nc.x = dot(hash(s), c0); + nc.y = dot(hash(s + offset1), c1); + nc.z = dot(hash(s + offset2), c2); + nc.w = dot(hash(s + offset3), c3); + + nc *= w*w*w*w; + + // Add all the noise contributions. + // Should multiply by the possible max contribution to adjust the range in [-1,1]. + return dot(vec4(32.), nc); + } + + // Random rotations. + // The way you create fractal noise is layering simplex noise with some rotation. + // To make random cloud looking noise, the rotations should not align. (Otherwise it + // creates patterned noise). + // Below rotations only rotate in one axis. + const mat3 rot1 = mat3(1.0, 0. ,0., 0., 0.15, -0.98, 0., 0.98, 0.15); + const mat3 rot2 = mat3(-0.95, 0. ,-0.3, 0., 1., 0., 0.3, 0., -0.95); + const mat3 rot3 = mat3(1.0, 0. ,0., 0., -0.44, -0.89, 0., 0.89, -0.44); + + // Octave = 4 + // Divide each coefficient by 3 to produce more grainy noise. + half simplex3d_fractal(vec3 p) { + return 0.675 * simplex3d(p * rot1) + 0.225 * simplex3d(2.0 * p * rot2) + + 0.075 * simplex3d(4.0 * p * rot3) + 0.025 * simplex3d(8.0 * p); + } + + // Screen blend + vec3 screen(vec3 dest, vec3 src) { + return dest + src - dest * src; + } + """ } diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt index 0e2266734274..d1ba7c4de35c 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt @@ -36,6 +36,7 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : uniform float in_aspectRatio; uniform float in_opacity; uniform float in_pixelDensity; + uniform float in_inverseLuma; layout(color) uniform vec4 in_color; layout(color) uniform vec4 in_backgroundColor; """ @@ -47,7 +48,7 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : uv.x *= in_aspectRatio; vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum; - float luma = simplex3d(noiseP) * in_opacity; + float luma = abs(in_inverseLuma - simplex3d(noiseP)) * in_opacity; vec3 mask = maskLuminosity(in_color.rgb, luma); vec3 color = in_backgroundColor.rgb + mask * 0.6; @@ -69,7 +70,7 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : uv.x *= in_aspectRatio; vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum; - float luma = simplex3d_fractal(noiseP) * in_opacity; + float luma = abs(in_inverseLuma - simplex3d_fractal(noiseP)) * in_opacity; vec3 mask = maskLuminosity(in_color.rgb, luma); vec3 color = in_backgroundColor.rgb + mask * 0.6; @@ -123,6 +124,17 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : setFloatUniform("in_aspectRatio", width / max(height, 0.001f)) } + /** + * Sets whether to inverse the luminosity of the noise. + * + * By default noise will be used as a luma matte as is. This means that you will see color in + * the brighter area. If you want to invert it, meaning blend color onto the darker side, set to + * true. + */ + fun setInverseNoiseLuminosity(inverse: Boolean) { + setFloatUniform("in_inverseLuma", if (inverse) 1f else 0f) + } + /** Current noise movements in x, y, and z axes. */ var noiseOffsetX: Float = 0f private set diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 00c0a0b3e7b3..e73afe74c03d 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -29,14 +29,15 @@ import com.android.systemui.plugins.ClockMetadata import com.android.systemui.plugins.ClockProvider import com.android.systemui.plugins.ClockProviderPlugin import com.android.systemui.plugins.ClockSettings +import com.android.systemui.plugins.PluginLifecycleManager import com.android.systemui.plugins.PluginListener import com.android.systemui.plugins.PluginManager import com.android.systemui.util.Assert +import java.util.concurrent.atomic.AtomicBoolean import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -private val TAG = ClockRegistry::class.simpleName!! private const val DEBUG = true private val KEY_TIMESTAMP = "appliedTimestamp" @@ -51,7 +52,10 @@ open class ClockRegistry( val handleAllUsers: Boolean, defaultClockProvider: ClockProvider, val fallbackClockId: ClockId = DEFAULT_CLOCK_ID, + val keepAllLoaded: Boolean, + val subTag: String, ) { + private val TAG = "${ClockRegistry::class.simpleName} ($subTag)" interface ClockChangeListener { // Called when the active clock changes fun onCurrentClockChanged() {} @@ -76,11 +80,85 @@ open class ClockRegistry( private val pluginListener = object : PluginListener<ClockProviderPlugin> { - override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) = - connectClocks(plugin) + override fun onPluginAttached(manager: PluginLifecycleManager<ClockProviderPlugin>) { + manager.loadPlugin() + } + + override fun onPluginLoaded( + plugin: ClockProviderPlugin, + pluginContext: Context, + manager: PluginLifecycleManager<ClockProviderPlugin> + ) { + var isClockListChanged = false + for (clock in plugin.getClocks()) { + val id = clock.clockId + var isNew = false + val info = + availableClocks.getOrPut(id) { + isNew = true + ClockInfo(clock, plugin, manager) + } + + if (isNew) { + isClockListChanged = true + onConnected(id) + } + + if (manager != info.manager) { + Log.e( + TAG, + "Clock Id conflict on load: $id is registered to another provider" + ) + continue + } + + info.provider = plugin + onLoaded(id) + } + + if (isClockListChanged) { + triggerOnAvailableClocksChanged() + } + verifyLoadedProviders() + } - override fun onPluginDisconnected(plugin: ClockProviderPlugin) = - disconnectClocks(plugin) + override fun onPluginUnloaded( + plugin: ClockProviderPlugin, + manager: PluginLifecycleManager<ClockProviderPlugin> + ) { + for (clock in plugin.getClocks()) { + val id = clock.clockId + val info = availableClocks[id] + if (info?.manager != manager) { + Log.e( + TAG, + "Clock Id conflict on unload: $id is registered to another provider" + ) + continue + } + info.provider = null + onUnloaded(id) + } + + verifyLoadedProviders() + } + + override fun onPluginDetached(manager: PluginLifecycleManager<ClockProviderPlugin>) { + val removed = mutableListOf<ClockId>() + availableClocks.entries.removeAll { + if (it.value.manager != manager) { + return@removeAll false + } + + removed.add(it.key) + return@removeAll true + } + + removed.forEach(::onDisconnected) + if (removed.size > 0) { + triggerOnAvailableClocksChanged() + } + } } private val userSwitchObserver = @@ -96,7 +174,8 @@ open class ClockRegistry( protected set(value) { if (field != value) { field = value - scope.launch(mainDispatcher) { onClockChanged { it.onCurrentClockChanged() } } + verifyLoadedProviders() + triggerOnCurrentClockChanged() } } @@ -168,9 +247,36 @@ open class ClockRegistry( Assert.isNotMainThread() } - private fun onClockChanged(func: (ClockChangeListener) -> Unit) { - assertMainThread() - clockChangeListeners.forEach(func) + private var isClockChanged = AtomicBoolean(false) + private fun triggerOnCurrentClockChanged() { + val shouldSchedule = isClockChanged.compareAndSet(false, true) + if (!shouldSchedule) { + return + } + + android.util.Log.e("HAWK", "triggerOnCurrentClockChanged") + scope.launch(mainDispatcher) { + assertMainThread() + android.util.Log.e("HAWK", "isClockChanged") + isClockChanged.set(false) + clockChangeListeners.forEach { it.onCurrentClockChanged() } + } + } + + private var isClockListChanged = AtomicBoolean(false) + private fun triggerOnAvailableClocksChanged() { + val shouldSchedule = isClockListChanged.compareAndSet(false, true) + if (!shouldSchedule) { + return + } + + android.util.Log.e("HAWK", "triggerOnAvailableClocksChanged") + scope.launch(mainDispatcher) { + assertMainThread() + android.util.Log.e("HAWK", "isClockListChanged") + isClockListChanged.set(false) + clockChangeListeners.forEach { it.onAvailableClocksChanged() } + } } public fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) { @@ -190,7 +296,12 @@ open class ClockRegistry( } init { - connectClocks(defaultClockProvider) + // Register default clock designs + for (clock in defaultClockProvider.getClocks()) { + availableClocks[clock.clockId] = ClockInfo(clock, defaultClockProvider, null) + } + + // Something has gone terribly wrong if the default clock isn't present if (!availableClocks.containsKey(DEFAULT_CLOCK_ID)) { throw IllegalArgumentException( "$defaultClockProvider did not register clock at $DEFAULT_CLOCK_ID" @@ -244,59 +355,87 @@ open class ClockRegistry( } } - private fun connectClocks(provider: ClockProvider) { - var isAvailableChanged = false - val currentId = currentClockId - for (clock in provider.getClocks()) { - val id = clock.clockId - val current = availableClocks[id] - if (current != null) { - Log.e( - TAG, - "Clock Id conflict: $id is registered by both " + - "${provider::class.simpleName} and ${current.provider::class.simpleName}" - ) - continue - } + private var isVerifying = AtomicBoolean(false) + private fun verifyLoadedProviders() { + val shouldSchedule = isVerifying.compareAndSet(false, true) + if (!shouldSchedule) { + return + } - availableClocks[id] = ClockInfo(clock, provider) - isAvailableChanged = true - if (DEBUG) { - Log.i(TAG, "Added ${clock.clockId}") + scope.launch(bgDispatcher) { + if (keepAllLoaded) { + // Enforce that all plugins are loaded if requested + for ((_, info) in availableClocks) { + info.manager?.loadPlugin() + } + isVerifying.set(false) + return@launch } - if (currentId == id) { - if (DEBUG) { - Log.i(TAG, "Current clock ($currentId) was connected") + val currentClock = availableClocks[currentClockId] + if (currentClock == null) { + // Current Clock missing, load no plugins and use default + for ((_, info) in availableClocks) { + info.manager?.unloadPlugin() } - onClockChanged { it.onCurrentClockChanged() } + isVerifying.set(false) + return@launch } - } - if (isAvailableChanged) { - onClockChanged { it.onAvailableClocksChanged() } + val currentManager = currentClock.manager + currentManager?.loadPlugin() + + for ((_, info) in availableClocks) { + val manager = info.manager + if (manager != null && manager.isLoaded && currentManager != manager) { + manager.unloadPlugin() + } + } + isVerifying.set(false) } } - private fun disconnectClocks(provider: ClockProvider) { - var isAvailableChanged = false - val currentId = currentClockId - for (clock in provider.getClocks()) { - availableClocks.remove(clock.clockId) - isAvailableChanged = true + private fun onConnected(clockId: ClockId) { + if (DEBUG) { + Log.i(TAG, "Connected $clockId") + } + if (currentClockId == clockId) { if (DEBUG) { - Log.i(TAG, "Removed ${clock.clockId}") + Log.i(TAG, "Current clock ($clockId) was connected") } + } + } - if (currentId == clock.clockId) { - Log.w(TAG, "Current clock ($currentId) was disconnected") - onClockChanged { it.onCurrentClockChanged() } - } + private fun onLoaded(clockId: ClockId) { + if (DEBUG) { + Log.i(TAG, "Loaded $clockId") + } + + if (currentClockId == clockId) { + Log.i(TAG, "Current clock ($clockId) was loaded") + triggerOnCurrentClockChanged() + } + } + + private fun onUnloaded(clockId: ClockId) { + if (DEBUG) { + Log.i(TAG, "Unloaded $clockId") + } + + if (currentClockId == clockId) { + Log.w(TAG, "Current clock ($clockId) was unloaded") + triggerOnCurrentClockChanged() + } + } + + private fun onDisconnected(clockId: ClockId) { + if (DEBUG) { + Log.i(TAG, "Disconnected $clockId") } - if (isAvailableChanged) { - onClockChanged { it.onAvailableClocksChanged() } + if (currentClockId == clockId) { + Log.w(TAG, "Current clock ($clockId) was disconnected") } } @@ -345,6 +484,7 @@ open class ClockRegistry( private data class ClockInfo( val metadata: ClockMetadata, - val provider: ClockProvider, + var provider: ClockProvider?, + val manager: PluginLifecycleManager<ClockProviderPlugin>?, ) } diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java new file mode 100644 index 000000000000..cc6a46fa7d6b --- /dev/null +++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.plugins; + +/** + * Provides the ability for consumers to control plugin lifecycle. + * + * @param <T> is the target plugin type + */ +public interface PluginLifecycleManager<T extends Plugin> { + /** Returns the currently loaded plugin instance (if plugin is loaded) */ + T getPlugin(); + + /** returns true if the plugin is currently loaded */ + default boolean isLoaded() { + return getPlugin() != null; + } + + /** + * Loads and creates the plugin instance if it does not exist. + * + * This will trigger {@link PluginListener#onPluginLoaded} with the new instance if it did not + * already exist. + */ + void loadPlugin(); + + /** + * Unloads and destroys the plugin instance if it exists. + * + * This will trigger {@link PluginListener#onPluginUnloaded} if a concrete plugin instance + * existed when this call was made. + */ + void unloadPlugin(); +} diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java index b488d2a84baa..c5f503216101 100644 --- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java +++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java @@ -17,7 +17,32 @@ package com.android.systemui.plugins; import android.content.Context; /** - * Interface for listening to plugins being connected. + * Interface for listening to plugins being connected and disconnected. + * + * The call order for a plugin is + * 1) {@link #onPluginAttached} + * Called when a new plugin is added to the device, or an existing plugin was replaced by + * the package manager. Will only be called once per package manager event. If multiple + * non-conflicting packages which have the same plugin interface are installed on the + * device, then this method can be called multiple times with different instances of + * {@link PluginLifecycleManager} (as long as `allowMultiple` was set to true when the + * listener was registered with {@link PluginManager#addPluginListener}). + * 2) {@link #onPluginLoaded} + * Called whenever a new instance of the plugin object is created and ready for use. Can be + * called multiple times per {@link PluginLifecycleManager}, but will always pass a newly + * created plugin object. {@link #onPluginUnloaded} with the previous plugin object will + * be called before another call to {@link #onPluginLoaded} is made. This method will be + * called once automatically after {@link #onPluginAttached}. Besides the initial call, + * {@link #onPluginLoaded} will occur due to {@link PluginLifecycleManager#loadPlugin}. + * 3) {@link #onPluginUnloaded} + * Called when a request to unload the plugin has been received. This can be triggered from + * a related call to {@link PluginLifecycleManager#unloadPlugin} or for any reason that + * {@link #onPluginDetached} would be triggered. + * 4) {@link #onPluginDetached} + * Called when the package is removed from the device, disabled, or replaced due to an + * external trigger. These are events from the android package manager. + * + * @param <T> is the target plugin type */ public interface PluginListener<T extends Plugin> { /** @@ -25,14 +50,69 @@ public interface PluginListener<T extends Plugin> { * This may be called multiple times if multiple plugins are allowed. * It may also be called in the future if the plugin package changes * and needs to be reloaded. + * + * @deprecated Migrate to {@link #onPluginLoaded} or {@link #onPluginAttached} + */ + @Deprecated + default void onPluginConnected(T plugin, Context pluginContext) { + // Optional + } + + /** + * Called when the plugin is first attached to the host application. {@link #onPluginLoaded} + * will be automatically called as well when first attached. This may be called multiple times + * if multiple plugins are allowed. It may also be called in the future if the plugin package + * changes and needs to be reloaded. Each call to {@link #onPluginAttached} will provide a new + * or different {@link PluginLifecycleManager}. */ - void onPluginConnected(T plugin, Context pluginContext); + default void onPluginAttached(PluginLifecycleManager<T> manager) { + // Optional + } /** * Called when a plugin has been uninstalled/updated and should be removed * from use. + * + * @deprecated Migrate to {@link #onPluginDetached} or {@link #onPluginUnloaded} */ + @Deprecated default void onPluginDisconnected(T plugin) { // Optional. } -} + + /** + * Called when the plugin has been detached from the host application. Implementers should no + * longer attempt to reload it via this {@link PluginLifecycleManager}. If the package was + * updated and not removed, then {@link #onPluginAttached} will be called again when the updated + * package is available. + */ + default void onPluginDetached(PluginLifecycleManager<T> manager) { + // Optional. + } + + /** + * Called when the plugin is loaded into the host's process and is available for use. This can + * happen several times if clients are using {@link PluginLifecycleManager} to manipulate a + * plugin's load state. Each call to {@link #onPluginLoaded} will have a matched call to + * {@link #onPluginUnloaded} when that plugin object should no longer be used. + */ + default void onPluginLoaded( + T plugin, + Context pluginContext, + PluginLifecycleManager<T> manager + ) { + // Optional, default to deprecated version + onPluginConnected(plugin, pluginContext); + } + + /** + * Called when the plugin should no longer be used. Listeners should clean up all references to + * the relevant plugin so that it can be garbage collected. If the plugin object is required in + * the future a call can be made to {@link PluginLifecycleManager#loadPlugin} to create a new + * plugin object and trigger {@link #onPluginLoaded}. + */ + default void onPluginUnloaded(T plugin, PluginLifecycleManager<T> manager) { + // Optional, default to deprecated version + onPluginDisconnected(plugin); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml index 9b2f0ac364da..79948da978d7 100644 --- a/packages/SystemUI/res/layout/notification_conversation_info.xml +++ b/packages/SystemUI/res/layout/notification_conversation_info.xml @@ -311,7 +311,7 @@ android:clickable="false" android:focusable="false" android:ellipsize="end" - android:maxLines="3" + android:maxLines="4" android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/> </com.android.systemui.statusbar.notification.row.ButtonLinearLayout> @@ -364,7 +364,7 @@ android:clickable="false" android:focusable="false" android:ellipsize="end" - android:maxLines="3" + android:maxLines="4" android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/> </com.android.systemui.statusbar.notification.row.ButtonLinearLayout> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 6354752e1b22..763930db1d55 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -30,9 +30,6 @@ <bool name="flag_charging_ripple">false</bool> - <!-- Whether to show chipbar UI whenever the device is unlocked by ActiveUnlock. --> - <bool name="flag_active_unlock_chipbar">true</bool> - <!-- Whether the user switcher chip shows in the status bar. When true, the multi user avatar will no longer show on the lockscreen --> <bool name="flag_user_switcher_chip">false</bool> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ebf0f8ecd48c..2aa912ce250d 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2877,7 +2877,7 @@ <string name="manage_users">Manage users</string> <!-- Toast shown when a notification does not support dragging to split [CHAR LIMIT=NONE] --> - <string name="drag_split_not_supported">This notification does not support dragging to Splitscreen.</string> + <string name="drag_split_not_supported">This notification does not support dragging to split screen</string> <!-- Content description for the Wi-Fi off icon in the dream overlay status bar [CHAR LIMIT=NONE] --> <string name="dream_overlay_status_bar_wifi_off">Wi\u2011Fi unavailable</string> diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt index 97665145ce76..dedf0a7a742d 100644 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt @@ -53,6 +53,7 @@ import kotlinx.coroutines.runBlocking fun View.captureToBitmap(window: Window? = null): ListenableFuture<Bitmap> { val bitmapFuture: ResolvableFuture<Bitmap> = ResolvableFuture.create() val mainExecutor = HandlerExecutor(Handler(Looper.getMainLooper())) + val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false // disable drawing again if necessary once work is complete if (!HardwareRendererCompat.isDrawingEnabled()) { @@ -61,8 +62,12 @@ fun View.captureToBitmap(window: Window? = null): ListenableFuture<Bitmap> { } mainExecutor.execute { - val forceRedrawFuture = forceRedraw() - forceRedrawFuture.addListener({ generateBitmap(bitmapFuture, window) }, mainExecutor) + if (isRobolectric) { + generateBitmap(bitmapFuture) + } else { + val forceRedrawFuture = forceRedraw() + forceRedrawFuture.addListener({ generateBitmap(bitmapFuture, window) }, mainExecutor) + } } return bitmapFuture diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt index 2d47356f2ce1..f96d1e3d7fef 100644 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt @@ -19,6 +19,7 @@ package com.android.systemui.testing.screenshot import android.app.Activity import android.app.Dialog import android.graphics.Bitmap +import android.os.Build import android.view.View import android.view.ViewGroup import android.view.ViewGroup.LayoutParams @@ -26,6 +27,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.activity.ComponentActivity import androidx.test.ext.junit.rules.ActivityScenarioRule +import java.util.concurrent.TimeUnit import org.junit.Assert.assertEquals import org.junit.rules.RuleChain import org.junit.rules.TestRule @@ -54,14 +56,14 @@ open class ViewScreenshotTestRule( ) ) private val activityRule = ActivityScenarioRule(ScreenshotActivity::class.java) - private val delegateRule = - RuleChain.outerRule(colorsRule) - .around(deviceEmulationRule) - .around(screenshotRule) - .around(activityRule) + private val roboRule = + RuleChain.outerRule(deviceEmulationRule).around(screenshotRule).around(activityRule) + private val delegateRule = RuleChain.outerRule(colorsRule).around(roboRule) + private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false override fun apply(base: Statement, description: Description): Statement { - return delegateRule.apply(base, description) + val ruleToApply = if (isRobolectric) roboRule else delegateRule + return ruleToApply.apply(base, description) } protected fun takeScreenshot( @@ -94,7 +96,12 @@ open class ViewScreenshotTestRule( contentView = content.getChildAt(0) } - return contentView?.toBitmap() ?: error("contentView is null") + return if (isRobolectric) { + contentView?.captureToBitmap()?.get(10, TimeUnit.SECONDS) + ?: error("timeout while trying to capture view to bitmap") + } else { + contentView?.toBitmap() ?: error("contentView is null") + } } /** diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java index cc3d7a8931b0..3d05542116e0 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java @@ -210,12 +210,12 @@ public class PluginActionManager<T extends Plugin> { private void onPluginConnected(PluginInstance<T> pluginInstance) { if (DEBUG) Log.d(TAG, "onPluginConnected"); PluginPrefs.setHasPlugins(mContext); - pluginInstance.onCreate(mContext, mListener); + pluginInstance.onCreate(); } private void onPluginDisconnected(PluginInstance<T> pluginInstance) { if (DEBUG) Log.d(TAG, "onPluginDisconnected"); - pluginInstance.onDestroy(mListener); + pluginInstance.onDestroy(); } private void queryAll() { @@ -312,7 +312,7 @@ public class PluginActionManager<T extends Plugin> { try { return mPluginInstanceFactory.create( mContext, appInfo, component, - mPluginClass); + mPluginClass, mListener); } catch (InvalidVersionException e) { reportInvalidVersion(component, component.getClassName(), e); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java index 2f84602089e0..016d573930e8 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java @@ -21,13 +21,16 @@ import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginFragment; +import com.android.systemui.plugins.PluginLifecycleManager; import com.android.systemui.plugins.PluginListener; import dalvik.system.PathClassLoader; @@ -35,7 +38,7 @@ import dalvik.system.PathClassLoader; import java.io.File; import java.util.ArrayList; import java.util.List; -import java.util.Map; +import java.util.function.Supplier; /** * Contains a single instantiation of a Plugin. @@ -45,42 +48,102 @@ import java.util.Map; * * @param <T> The type of plugin that this contains. */ -public class PluginInstance<T extends Plugin> { +public class PluginInstance<T extends Plugin> implements PluginLifecycleManager { private static final String TAG = "PluginInstance"; - private static final Map<String, ClassLoader> sClassLoaders = new ArrayMap<>(); - private final Context mPluginContext; - private final VersionInfo mVersionInfo; + private final Context mAppContext; + private final PluginListener<T> mListener; private final ComponentName mComponentName; - private final T mPlugin; + private final PluginFactory<T> mPluginFactory; + + private Context mPluginContext; + private T mPlugin; /** */ - public PluginInstance(ComponentName componentName, T plugin, Context pluginContext, - VersionInfo versionInfo) { + public PluginInstance( + Context appContext, + PluginListener<T> listener, + ComponentName componentName, + PluginFactory<T> pluginFactory, + @Nullable T plugin) { + mAppContext = appContext; + mListener = listener; mComponentName = componentName; + mPluginFactory = pluginFactory; mPlugin = plugin; - mPluginContext = pluginContext; - mVersionInfo = versionInfo; + + if (mPlugin != null) { + mPluginContext = mPluginFactory.createPluginContext(); + } } /** Alerts listener and plugin that the plugin has been created. */ - public void onCreate(Context appContext, PluginListener<T> listener) { + public void onCreate() { + mListener.onPluginAttached(this); + if (mPlugin == null) { + loadPlugin(); + } else { + if (!(mPlugin instanceof PluginFragment)) { + // Only call onCreate for plugins that aren't fragments, as fragments + // will get the onCreate as part of the fragment lifecycle. + mPlugin.onCreate(mAppContext, mPluginContext); + } + mListener.onPluginLoaded(mPlugin, mPluginContext, this); + } + } + + /** Alerts listener and plugin that the plugin is being shutdown. */ + public void onDestroy() { + unloadPlugin(); + mListener.onPluginDetached(this); + } + + /** Returns the current plugin instance (if it is loaded). */ + @Nullable + public T getPlugin() { + return mPlugin; + } + + /** + * Loads and creates the plugin if it does not exist. + */ + public void loadPlugin() { + if (mPlugin != null) { + return; + } + + mPlugin = mPluginFactory.createPlugin(); + mPluginContext = mPluginFactory.createPluginContext(); + if (mPlugin == null || mPluginContext == null) { + return; + } + if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments // will get the onCreate as part of the fragment lifecycle. - mPlugin.onCreate(appContext, mPluginContext); + mPlugin.onCreate(mAppContext, mPluginContext); } - listener.onPluginConnected(mPlugin, mPluginContext); + mListener.onPluginLoaded(mPlugin, mPluginContext, this); } - /** Alerts listener and plugin that the plugin is being shutdown. */ - public void onDestroy(PluginListener<T> listener) { - listener.onPluginDisconnected(mPlugin); + /** + * Unloads and destroys the current plugin instance if it exists. + * + * This will free the associated memory if there are not other references. + */ + public void unloadPlugin() { + if (mPlugin == null) { + return; + } + + mListener.onPluginUnloaded(mPlugin, this); if (!(mPlugin instanceof PluginFragment)) { // Only call onDestroy for plugins that aren't fragments, as fragments // will get the onDestroy as part of the fragment lifecycle. mPlugin.onDestroy(); } + mPlugin = null; + mPluginContext = null; } /** @@ -89,7 +152,7 @@ public class PluginInstance<T extends Plugin> { * It does this by string comparison of the class names. **/ public boolean containsPluginClass(Class pluginClass) { - return mPlugin.getClass().getName().equals(pluginClass.getName()); + return mComponentName.getClassName().equals(pluginClass.getName()); } public ComponentName getComponentName() { @@ -101,7 +164,7 @@ public class PluginInstance<T extends Plugin> { } public VersionInfo getVersionInfo() { - return mVersionInfo; + return mPluginFactory.checkVersion(mPlugin); } @VisibleForTesting @@ -134,21 +197,20 @@ public class PluginInstance<T extends Plugin> { Context context, ApplicationInfo appInfo, ComponentName componentName, - Class<T> pluginClass) + Class<T> pluginClass, + PluginListener<T> listener) throws PackageManager.NameNotFoundException, ClassNotFoundException, InstantiationException, IllegalAccessException { - ClassLoader classLoader = getClassLoader(appInfo, mBaseClassLoader); - Context pluginContext = new PluginActionManager.PluginContextWrapper( - context.createApplicationContext(appInfo, 0), classLoader); - Class<T> instanceClass = (Class<T>) Class.forName( - componentName.getClassName(), true, classLoader); + PluginFactory<T> pluginFactory = new PluginFactory<T>( + context, mInstanceFactory, appInfo, componentName, mVersionChecker, pluginClass, + () -> getClassLoader(appInfo, mBaseClassLoader)); // TODO: Only create the plugin before version check if we need it for // legacy version check. - T instance = (T) mInstanceFactory.create(instanceClass); - VersionInfo version = mVersionChecker.checkVersion( - instanceClass, pluginClass, instance); - return new PluginInstance<T>(componentName, instance, pluginContext, version); + T instance = pluginFactory.createPlugin(); + pluginFactory.checkVersion(instance); + return new PluginInstance<T>( + context, listener, componentName, pluginFactory, instance); } private boolean isPluginPackagePrivileged(String packageName) { @@ -179,9 +241,6 @@ public class PluginInstance<T extends Plugin> { + appInfo.sourceDir + ", pkg: " + appInfo.packageName); return null; } - if (sClassLoaders.containsKey(appInfo.packageName)) { - return sClassLoaders.get(appInfo.packageName); - } List<String> zipPaths = new ArrayList<>(); List<String> libPaths = new ArrayList<>(); @@ -190,13 +249,20 @@ public class PluginInstance<T extends Plugin> { TextUtils.join(File.pathSeparator, zipPaths), TextUtils.join(File.pathSeparator, libPaths), getParentClassLoader(baseClassLoader)); - sClassLoaders.put(appInfo.packageName, classLoader); return classLoader; } } /** Class that compares a plugin class against an implementation for version matching. */ - public static class VersionChecker { + public interface VersionChecker { + /** Compares two plugin classes. */ + <T extends Plugin> VersionInfo checkVersion( + Class<T> instanceClass, Class<T> pluginClass, Plugin plugin); + } + + /** Class that compares a plugin class against an implementation for version matching. */ + public static class VersionCheckerImpl implements VersionChecker { + @Override /** Compares two plugin classes. */ public <T extends Plugin> VersionInfo checkVersion( Class<T> instanceClass, Class<T> pluginClass, Plugin plugin) { @@ -204,7 +270,7 @@ public class PluginInstance<T extends Plugin> { VersionInfo instanceVersion = new VersionInfo().addClass(instanceClass); if (instanceVersion.hasVersionInfo()) { pluginVersion.checkVersion(instanceVersion); - } else { + } else if (plugin != null) { int fallbackVersion = plugin.getVersion(); if (fallbackVersion != pluginVersion.getDefaultVersion()) { throw new VersionInfo.InvalidVersionException("Invalid legacy version", false); @@ -225,4 +291,74 @@ public class PluginInstance<T extends Plugin> { return (T) cls.newInstance(); } } + + /** + * Instanced wrapper of InstanceFactory + * + * @param <T> is the type of the plugin object to be built + **/ + public static class PluginFactory<T extends Plugin> { + private final Context mContext; + private final InstanceFactory<?> mInstanceFactory; + private final ApplicationInfo mAppInfo; + private final ComponentName mComponentName; + private final VersionChecker mVersionChecker; + private final Class<T> mPluginClass; + private final Supplier<ClassLoader> mClassLoaderFactory; + + public PluginFactory( + Context context, + InstanceFactory<?> instanceFactory, + ApplicationInfo appInfo, + ComponentName componentName, + VersionChecker versionChecker, + Class<T> pluginClass, + Supplier<ClassLoader> classLoaderFactory) { + mContext = context; + mInstanceFactory = instanceFactory; + mAppInfo = appInfo; + mComponentName = componentName; + mVersionChecker = versionChecker; + mPluginClass = pluginClass; + mClassLoaderFactory = classLoaderFactory; + } + + /** Creates the related plugin object from the factory */ + public T createPlugin() { + try { + ClassLoader loader = mClassLoaderFactory.get(); + Class<T> instanceClass = (Class<T>) Class.forName( + mComponentName.getClassName(), true, loader); + return (T) mInstanceFactory.create(instanceClass); + } catch (ClassNotFoundException ex) { + Log.e(TAG, "Failed to load plugin", ex); + } catch (IllegalAccessException ex) { + Log.e(TAG, "Failed to load plugin", ex); + } catch (InstantiationException ex) { + Log.e(TAG, "Failed to load plugin", ex); + } + return null; + } + + /** Creates a context wrapper for the plugin */ + public Context createPluginContext() { + try { + ClassLoader loader = mClassLoaderFactory.get(); + return new PluginActionManager.PluginContextWrapper( + mContext.createApplicationContext(mAppInfo, 0), loader); + } catch (NameNotFoundException ex) { + Log.e(TAG, "Failed to create plugin context", ex); + } + return null; + } + + /** Check Version and create VersionInfo for instance */ + public VersionInfo checkVersion(T instance) { + if (instance == null) { + instance = createPlugin(); + } + return mVersionChecker.checkVersion( + (Class<T>) instance.getClass(), mPluginClass, instance); + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java index b1a83fbda7de..6e98a1805d62 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java @@ -60,7 +60,9 @@ public abstract class ClockRegistryModule { featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS), /* handleAllUsers= */ true, new DefaultClockProvider(context, layoutInflater, resources), - context.getString(R.string.lockscreen_clock_id_fallback)); + context.getString(R.string.lockscreen_clock_id_fallback), + /* keepAllLoaded = */ false, + /* subTag = */ "System"); registry.registerListeners(); return registry; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index a7b6e6ae6d40..13bb6d345dbd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -658,7 +658,9 @@ public abstract class AuthBiometricView extends LinearLayout { protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mIconController.onConfigurationChanged(newConfig); - updateState(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_STATE)); + if (mSavedState != null) { + updateState(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_STATE)); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 661b2b6daf64..a80d28d7877f 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -162,12 +162,6 @@ object Flags { val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES = releasedFlag(216, "customizable_lock_screen_quick_affordances") - /** Shows chipbar UI whenever the device is unlocked by ActiveUnlock (watch). */ - // TODO(b/256513609): Tracking Bug - @JvmField - val ACTIVE_UNLOCK_CHIPBAR = - resourceBooleanFlag(217, R.bool.flag_active_unlock_chipbar, "active_unlock_chipbar") - /** * Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the * new KeyguardTransitionRepository. @@ -242,12 +236,21 @@ object Flags { // TODO(b/270223352): Tracking Bug @JvmField - val HIDE_SMARTSPACE_ON_DREAM_OVERLAY = unreleasedFlag(404, "hide_smartspace_on_dream_overlay") + val HIDE_SMARTSPACE_ON_DREAM_OVERLAY = + unreleasedFlag( + 404, + "hide_smartspace_on_dream_overlay", + teamfood = true + ) // TODO(b/271460958): Tracking Bug @JvmField - val SHOW_WEATHER_COMPLICATION_ON_DREAM_OVERLAY = unreleasedFlag(405, - "show_weather_complication_on_dream_overlay") + val SHOW_WEATHER_COMPLICATION_ON_DREAM_OVERLAY = + unreleasedFlag( + 405, + "show_weather_complication_on_dream_overlay", + teamfood = true + ) // 500 - quick settings @@ -284,8 +287,7 @@ object Flags { /** Enables new QS Edit Mode visual refresh */ // TODO(b/269787742): Tracking Bug @JvmField - val ENABLE_NEW_QS_EDIT_MODE = - unreleasedFlag(510, "enable_new_qs_edit_mode", teamfood = false) + val ENABLE_NEW_QS_EDIT_MODE = unreleasedFlag(510, "enable_new_qs_edit_mode", teamfood = false) // 600- status bar @@ -668,8 +670,7 @@ object Flags { // 2600 - keyboard // TODO(b/259352579): Tracking Bug - @JvmField val SHORTCUT_LIST_SEARCH_LAYOUT = - unreleasedFlag(2600, "shortcut_list_search_layout", teamfood = true) + @JvmField val SHORTCUT_LIST_SEARCH_LAYOUT = releasedFlag(2600, "shortcut_list_search_layout") // TODO(b/259428678): Tracking Bug @JvmField @@ -680,4 +681,9 @@ object Flags { @JvmField val LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION = unreleasedFlag(2602, "large_shade_granular_alpha_interpolation", teamfood = true) + + // TODO(b/272805037): Tracking Bug + @JvmField + val ADVANCED_VPN_ENABLED = unreleasedFlag(2800, name = "AdvancedVpn__enable_feature", + namespace = "vpn", teamfood = false) } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java index 95f14190537f..fbf1a0e46ce3 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java @@ -73,7 +73,7 @@ public abstract class PluginsModule { return new PluginInstance.Factory( PluginModule.class.getClassLoader(), new PluginInstance.InstanceFactory<>(), - new PluginInstance.VersionChecker(), + new PluginInstance.VersionCheckerImpl(), privilegedPlugins, isDebug); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index baa812c63aa7..584d27f84ceb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -635,7 +635,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca && mLastKeyguardAndExpanded == onKeyguardAndExpanded && mLastViewHeight == currentHeight && mLastHeaderTranslation == headerTranslation - && mSquishinessFraction == squishinessFraction) { + && mSquishinessFraction == squishinessFraction + && mLastPanelFraction == panelExpansionFraction) { return; } mLastHeaderTranslation = headerTranslation; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java index a71e6ddb6abd..9ece72d2ca7f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java @@ -26,6 +26,7 @@ import com.android.systemui.R; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTileView; +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.util.leak.GarbageMonitor; import java.util.ArrayList; @@ -33,7 +34,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -public interface QSHost { +public interface QSHost extends PanelInteractor { String TILES_SETTING = Settings.Secure.QS_TILES; int POSITION_AT_END = -1; @@ -57,9 +58,6 @@ public interface QSHost { } void warn(String message, Throwable t); - void collapsePanels(); - void forceCollapsePanels(); - void openPanels(); Context getContext(); Context getUserContext(); int getUserId(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt new file mode 100644 index 000000000000..958fa71b1fd8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.qs.dagger + +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QSTileHost +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl +import dagger.Binds +import dagger.Module +import dagger.Provides + +@Module +interface QSHostModule { + + @Binds fun provideQsHost(controllerImpl: QSTileHost): QSHost + + @Module + companion object { + @Provides + @JvmStatic + fun providePanelInteractor( + featureFlags: FeatureFlags, + qsHost: QSHost, + panelInteractorImpl: PanelInteractorImpl + ): PanelInteractor { + return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) { + panelInteractorImpl + } else { + qsHost + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java index 431d6e847207..cfe93132c044 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java @@ -27,7 +27,6 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.media.dagger.MediaModule; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSHost; -import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.ReduceBrightColorsController; import com.android.systemui.qs.external.QSExternalModule; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -45,7 +44,6 @@ import java.util.Map; import javax.inject.Named; -import dagger.Binds; import dagger.Module; import dagger.Provides; import dagger.multibindings.Multibinds; @@ -54,7 +52,13 @@ import dagger.multibindings.Multibinds; * Module for QS dependencies */ @Module(subcomponents = {QSFragmentComponent.class}, - includes = {MediaModule.class, QSExternalModule.class, QSFlagsModule.class}) + includes = { + MediaModule.class, + QSExternalModule.class, + QSFlagsModule.class, + QSHostModule.class + } +) public interface QSModule { /** A map of internal QS tiles. Ensures that this can be injected even if @@ -100,8 +104,4 @@ public interface QSModule { manager.init(); return manager; } - - /** */ - @Binds - QSHost provideQsHost(QSTileHost controllerImpl); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java index adc71657e680..2083cc7b167e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -40,6 +40,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -74,6 +75,7 @@ public class TileServices extends IQSService.Stub { private final CommandQueue mCommandQueue; private final UserTracker mUserTracker; private final StatusBarIconController mStatusBarIconController; + private final PanelInteractor mPanelInteractor; private int mMaxBound = DEFAULT_MAX_BOUND; @@ -85,7 +87,8 @@ public class TileServices extends IQSService.Stub { UserTracker userTracker, KeyguardStateController keyguardStateController, CommandQueue commandQueue, - StatusBarIconController statusBarIconController) { + StatusBarIconController statusBarIconController, + PanelInteractor panelInteractor) { mHost = host; mKeyguardStateController = keyguardStateController; mContext = mHost.getContext(); @@ -96,6 +99,7 @@ public class TileServices extends IQSService.Stub { mCommandQueue = commandQueue; mStatusBarIconController = statusBarIconController; mCommandQueue.addCallback(mRequestListeningCallback); + mPanelInteractor = panelInteractor; } public Context getContext() { @@ -255,7 +259,7 @@ public class TileServices extends IQSService.Stub { if (customTile != null) { verifyCaller(customTile); customTile.onDialogShown(); - mHost.forceCollapsePanels(); + mPanelInteractor.forceCollapsePanels(); Objects.requireNonNull(mServices.get(customTile)).setShowingDialog(true); } } @@ -275,7 +279,7 @@ public class TileServices extends IQSService.Stub { CustomTile customTile = getTileForToken(token); if (customTile != null) { verifyCaller(customTile); - mHost.forceCollapsePanels(); + mPanelInteractor.forceCollapsePanels(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractor.kt new file mode 100644 index 000000000000..260caa767a5e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractor.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.qs.pipeline.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.phone.CentralSurfaces +import java.util.Optional +import javax.inject.Inject + +/** Encapsulates business logic for interacting with the QS panel. */ +interface PanelInteractor { + + /** Collapse the shade */ + fun collapsePanels() + + /** Collapse the shade forcefully, skipping some animations. */ + fun forceCollapsePanels() + + /** Open the Quick Settings panel */ + fun openPanels() +} + +@SysUISingleton +class PanelInteractorImpl +@Inject +constructor( + private val centralSurfaces: Optional<CentralSurfaces>, +) : PanelInteractor { + override fun collapsePanels() { + centralSurfaces.ifPresent { it.postAnimateCollapsePanels() } + } + + override fun forceCollapsePanels() { + centralSurfaces.ifPresent { it.postAnimateForceCollapsePanels() } + } + + override fun openPanels() { + centralSurfaces.ifPresent { it.postAnimateOpenPanels() } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index 89d402a3af5a..27f58269722a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -38,6 +38,7 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.LocationController; @@ -52,6 +53,7 @@ public class LocationTile extends QSTileImpl<BooleanState> { private final LocationController mController; private final KeyguardStateController mKeyguard; + private final PanelInteractor mPanelInteractor; private final Callback mCallback = new Callback(); @Inject @@ -65,12 +67,14 @@ public class LocationTile extends QSTileImpl<BooleanState> { ActivityStarter activityStarter, QSLogger qsLogger, LocationController locationController, - KeyguardStateController keyguardStateController + KeyguardStateController keyguardStateController, + PanelInteractor panelInteractor ) { super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mController = locationController; mKeyguard = keyguardStateController; + mPanelInteractor = panelInteractor; mController.observe(this, mCallback); mKeyguard.observe(this, mCallback); } @@ -90,7 +94,7 @@ public class LocationTile extends QSTileImpl<BooleanState> { if (mKeyguard.isMethodSecure() && mKeyguard.isShowing()) { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { final boolean wasEnabled = mState.value; - mHost.openPanels(); + mPanelInteractor.openPanels(); mController.setLocationEnabled(!wasEnabled); }); return; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java index 07b50c9a66f6..65592a717565 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -42,6 +42,7 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; @@ -66,6 +67,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> private final Callback mCallback = new Callback(); private final DialogLaunchAnimator mDialogLaunchAnimator; private final FeatureFlags mFlags; + private final PanelInteractor mPanelInteractor; private long mMillisUntilFinished = 0; @@ -83,7 +85,8 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> RecordingController controller, KeyguardDismissUtil keyguardDismissUtil, KeyguardStateController keyguardStateController, - DialogLaunchAnimator dialogLaunchAnimator + DialogLaunchAnimator dialogLaunchAnimator, + PanelInteractor panelInteractor ) { super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); @@ -93,6 +96,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> mKeyguardDismissUtil = keyguardDismissUtil; mKeyguardStateController = keyguardStateController; mDialogLaunchAnimator = dialogLaunchAnimator; + mPanelInteractor = panelInteractor; } @Override @@ -171,7 +175,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> // disable the exit animation which looks weird when it happens at the same time as the // shade collapsing. mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); - getHost().collapsePanels(); + mPanelInteractor.collapsePanels(); }; final Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags, diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 587c65d44091..30865aa0c45d 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -3779,10 +3779,10 @@ public final class NotificationPanelViewController implements Dumpable { mHeightAnimator.end(); } } - mQsController.setShadeExpandedHeight(mExpandedHeight); - mExpansionDragDownAmountPx = h; mExpandedFraction = Math.min(1f, maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight); + mQsController.setShadeExpansion(mExpandedHeight, mExpandedFraction); + mExpansionDragDownAmountPx = h; mAmbientState.setExpansionFraction(mExpandedFraction); onHeightUpdated(mExpandedHeight); updatePanelExpansionAndVisibility(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index df8ae50682fc..9f467074d473 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -351,7 +351,6 @@ public class QuickSettingsController { mFeatureFlags = featureFlags; mInteractionJankMonitor = interactionJankMonitor; - mShadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged); mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback()); } @@ -878,8 +877,9 @@ public class QuickSettingsController { mCollapsedOnDown = collapsedOnDown; } - void setShadeExpandedHeight(float shadeExpandedHeight) { - mShadeExpandedHeight = shadeExpandedHeight; + void setShadeExpansion(float expandedHeight, float expandedFraction) { + mShadeExpandedHeight = expandedHeight; + mShadeExpandedFraction = expandedFraction; } @VisibleForTesting @@ -1749,11 +1749,6 @@ public class QuickSettingsController { return false; } - @VisibleForTesting - void onPanelExpansionChanged(ShadeExpansionChangeEvent event) { - mShadeExpandedFraction = event.getFraction(); - } - /** * Animate QS closing by flinging it. * If QS is expanded, it will collapse into QQS and stop. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java index 01e042bf5e15..c920e1ec6604 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java @@ -657,25 +657,6 @@ public final class KeyboardShortcutListSearch { R.string.input_switch_input_language_previous), KeyEvent.KEYCODE_SPACE, KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON), - null))), - /* Access emoji: Meta + . */ - new ShortcutMultiMappingInfo( - context.getString(R.string.input_access_emoji), - null, - Arrays.asList( - new ShortcutKeyGroup(new KeyboardShortcutInfo( - context.getString(R.string.input_access_emoji), - KeyEvent.KEYCODE_PERIOD, - KeyEvent.META_META_ON), - null))), - /* Access voice typing: Meta + V */ - new ShortcutMultiMappingInfo( - context.getString(R.string.input_access_voice_typing), - null, - Arrays.asList( - new ShortcutKeyGroup(new KeyboardShortcutInfo( - context.getString(R.string.input_access_voice_typing), - KeyEvent.KEYCODE_V, KeyEvent.META_META_ON), null))) ); return new KeyboardShortcutMultiMappingGroup( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 33cbf0699bca..a529da54fc4e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -68,6 +68,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.CallLayout; @@ -1671,7 +1672,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView MetricsLogger metricsLogger, SmartReplyConstants smartReplyConstants, SmartReplyController smartReplyController, - FeatureFlags featureFlags) { + FeatureFlags featureFlags, + IStatusBarService statusBarService) { mEntry = entry; mAppName = appName; if (mMenuRow == null) { @@ -1699,7 +1701,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mPeopleNotificationIdentifier, rivSubcomponentFactory, smartReplyConstants, - smartReplyController); + smartReplyController, + statusBarService); } mOnUserInteractionCallback = onUserInteractionCallback; mBubblesManagerOptional = bubblesManagerOptional; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index e2a3111fc08f..5ca0866209a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -29,6 +29,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.logging.MetricsLogger; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; @@ -100,6 +101,7 @@ public class ExpandableNotificationRowController implements NotifViewController private final SmartReplyController mSmartReplyController; private final ExpandableNotificationRowDragController mDragController; private final NotificationDismissibilityProvider mDismissibilityProvider; + private final IStatusBarService mStatusBarService; private final ExpandableNotificationRow.ExpandableNotificationRowLogger mLoggerCallback = new ExpandableNotificationRow.ExpandableNotificationRowLogger() { @Override @@ -157,7 +159,8 @@ public class ExpandableNotificationRowController implements NotifViewController PeopleNotificationIdentifier peopleNotificationIdentifier, Optional<BubblesManager> bubblesManagerOptional, ExpandableNotificationRowDragController dragController, - NotificationDismissibilityProvider dismissibilityProvider) { + NotificationDismissibilityProvider dismissibilityProvider, + IStatusBarService statusBarService) { mView = view; mListContainer = listContainer; mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory; @@ -189,6 +192,7 @@ public class ExpandableNotificationRowController implements NotifViewController mSmartReplyConstants = smartReplyConstants; mSmartReplyController = smartReplyController; mDismissibilityProvider = dismissibilityProvider; + mStatusBarService = statusBarService; } /** @@ -220,7 +224,8 @@ public class ExpandableNotificationRowController implements NotifViewController mMetricsLogger, mSmartReplyConstants, mSmartReplyController, - mFeatureFlags + mFeatureFlags, + mStatusBarService ); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 5834dcbf8645..78392f78428f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -21,10 +21,13 @@ import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; +import android.os.RemoteException; import android.provider.Settings; +import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.IndentingPrintWriter; @@ -39,6 +42,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.RemoteInputController; @@ -129,6 +133,7 @@ public class NotificationContentView extends FrameLayout implements Notification private Runnable mExpandedVisibleListener; private PeopleNotificationIdentifier mPeopleIdentifier; private RemoteInputViewSubcomponent.Factory mRemoteInputSubcomponentFactory; + private IStatusBarService mStatusBarService; /** * List of listeners for when content views become inactive (i.e. not the showing view). @@ -196,11 +201,13 @@ public class NotificationContentView extends FrameLayout implements Notification PeopleNotificationIdentifier peopleNotificationIdentifier, RemoteInputViewSubcomponent.Factory rivSubcomponentFactory, SmartReplyConstants smartReplyConstants, - SmartReplyController smartReplyController) { + SmartReplyController smartReplyController, + IStatusBarService statusBarService) { mPeopleIdentifier = peopleNotificationIdentifier; mRemoteInputSubcomponentFactory = rivSubcomponentFactory; mSmartReplyConstants = smartReplyConstants; mSmartReplyController = smartReplyController; + mStatusBarService = statusBarService; } public void reinflate() { @@ -2193,4 +2200,36 @@ public class NotificationContentView extends FrameLayout implements Notification protected void setHeadsUpWrapper(NotificationViewWrapper headsUpWrapper) { mHeadsUpWrapper = headsUpWrapper; } + + @Override + protected void dispatchDraw(Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + cancelNotification(e); + } + } + + private void cancelNotification(Exception exception) { + try { + setVisibility(GONE); + final StatusBarNotification sbn = mNotificationEntry.getSbn(); + if (mStatusBarService != null) { + // report notification inflation errors back up + // to notification delegates + mStatusBarService.onNotificationError( + sbn.getPackageName(), + sbn.getTag(), + sbn.getId(), + sbn.getUid(), + sbn.getInitialPid(), + exception.getMessage(), + sbn.getUser().getIdentifier()); + } + } catch (RemoteException ex) { + Log.e(TAG, "cancelNotification failed: " + ex); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 0bded7327ea6..46603df955d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -809,6 +809,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump public void setOccludeAnimationPlaying(boolean occludeAnimationPlaying) { mOccludeAnimationPlaying = occludeAnimationPlaying; + + for (ScrimState state : ScrimState.values()) { + state.setOccludeAnimationPlaying(occludeAnimationPlaying); + } + applyAndDispatchState(); } @@ -853,7 +858,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (mState == ScrimState.UNLOCKED || mState == ScrimState.DREAMING) { final boolean occluding = mOccludeAnimationPlaying || mState.mLaunchingAffordanceWithPreview; - // Darken scrim as it's pulled down while unlocked. If we're unlocked but playing the // screen off/occlusion animations, ignore expansion changes while those animations // play. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 0e9d3ce33d5b..7b2028310a84 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -243,7 +243,12 @@ public enum ScrimState { : CentralSurfaces.FADE_KEYGUARD_DURATION; boolean fromAod = previousState == AOD || previousState == PULSING; - mAnimateChange = !mLaunchingAffordanceWithPreview && !fromAod; + // If launch/occlude animations were playing, they already animated the scrim + // alpha to 0f as part of the animation. If we animate it now, we'll set it back + // to 1f and animate it back to 0f, causing an unwanted scrim flash. + mAnimateChange = !mLaunchingAffordanceWithPreview + && !mOccludeAnimationPlaying + && !fromAod; mFrontTint = Color.TRANSPARENT; mBehindTint = Color.BLACK; @@ -308,6 +313,7 @@ public enum ScrimState { boolean mWallpaperSupportsAmbientMode; boolean mHasBackdrop; boolean mLaunchingAffordanceWithPreview; + boolean mOccludeAnimationPlaying; boolean mWakeLockScreenSensorActive; boolean mKeyguardFadingAway; long mKeyguardFadingAwayDuration; @@ -411,6 +417,10 @@ public enum ScrimState { mLaunchingAffordanceWithPreview = launchingAffordanceWithPreview; } + public void setOccludeAnimationPlaying(boolean occludeAnimationPlaying) { + mOccludeAnimationPlaying = occludeAnimationPlaying; + } + public boolean isLowPowerState() { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java index a0a0372426ec..209ea41fed61 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java @@ -59,6 +59,7 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.MessageRouter; @@ -412,6 +413,7 @@ public class GarbageMonitor implements Dumpable { private final GarbageMonitor gm; private ProcessMemInfo pmi; private boolean dumpInProgress; + private final PanelInteractor mPanelInteractor; @Inject public MemoryTile( @@ -423,11 +425,13 @@ public class GarbageMonitor implements Dumpable { StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, - GarbageMonitor monitor + GarbageMonitor monitor, + PanelInteractor panelInteractor ) { super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); gm = monitor; + mPanelInteractor = panelInteractor; } @Override @@ -457,7 +461,7 @@ public class GarbageMonitor implements Dumpable { mHandler.post(() -> { dumpInProgress = false; refreshState(); - getHost().collapsePanels(); + mPanelInteractor.collapsePanels(); mActivityStarter.postStartActivityDismissingKeyguard(shareIntent, 0); }); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java index 64e9a3e58bd6..7e052bfa15d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.when; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Intent; +import android.os.Binder; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -41,6 +42,7 @@ import android.testing.TestableLooper.RunWithLooper; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -91,6 +93,8 @@ public class TileServicesTest extends SysuiTestCase { private TileLifecycleManager mTileLifecycleManager; @Mock private QSHost mQSHost; + @Mock + private PanelInteractor mPanelInteractor; @Before public void setUp() throws Exception { @@ -107,7 +111,8 @@ public class TileServicesTest extends SysuiTestCase { Provider<Handler> provider = () -> new Handler(mTestableLooper.getLooper()); mTileService = new TestTileServices(mQSHost, provider, mBroadcastDispatcher, - mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController); + mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController, + mPanelInteractor); } @After @@ -222,13 +227,37 @@ public class TileServicesTest extends SysuiTestCase { verify(tile, never()).startActivityAndCollapse(pi); } + @Test + public void testOnStartActivityCollapsesPanel() { + CustomTile tile = mock(CustomTile.class); + ComponentName componentName = mock(ComponentName.class); + when(tile.getComponent()).thenReturn(componentName); + when(componentName.getPackageName()).thenReturn(this.getContext().getPackageName()); + TileServiceManager manager = mTileService.getTileWrapper(tile); + + mTileService.onStartActivity(manager.getToken()); + verify(mPanelInteractor).forceCollapsePanels(); + } + + @Test + public void testOnShowDialogCollapsesPanel() { + CustomTile tile = mock(CustomTile.class); + ComponentName componentName = mock(ComponentName.class); + when(tile.getComponent()).thenReturn(componentName); + when(componentName.getPackageName()).thenReturn(this.getContext().getPackageName()); + TileServiceManager manager = mTileService.getTileWrapper(tile); + + mTileService.onShowDialog(manager.getToken()); + verify(mPanelInteractor).forceCollapsePanels(); + } + private class TestTileServices extends TileServices { TestTileServices(QSHost host, Provider<Handler> handlerProvider, BroadcastDispatcher broadcastDispatcher, UserTracker userTracker, KeyguardStateController keyguardStateController, CommandQueue commandQueue, - StatusBarIconController statusBarIconController) { + StatusBarIconController statusBarIconController, PanelInteractor panelInteractor) { super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController, - commandQueue, statusBarIconController); + commandQueue, statusBarIconController, panelInteractor); } @Override @@ -237,6 +266,8 @@ public class TileServicesTest extends SysuiTestCase { TileServiceManager manager = mock(TileServiceManager.class); mManagers.add(manager); when(manager.isLifecycleStarted()).thenReturn(true); + Binder b = new Binder(); + when(manager.getToken()).thenReturn(b); return manager; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt new file mode 100644 index 000000000000..45783abe9ee4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.qs.pipeline.domain.interactor + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.phone.CentralSurfaces +import java.util.Optional +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 + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class PanelInteractorImplTest : SysuiTestCase() { + + @Mock private lateinit var centralSurfaces: CentralSurfaces + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun openPanels_callsCentralSurfaces() { + val underTest = PanelInteractorImpl(Optional.of(centralSurfaces)) + + underTest.openPanels() + + verify(centralSurfaces).postAnimateOpenPanels() + } + + @Test + fun collapsePanels_callsCentralSurfaces() { + val underTest = PanelInteractorImpl(Optional.of(centralSurfaces)) + + underTest.collapsePanels() + + verify(centralSurfaces).postAnimateCollapsePanels() + } + + @Test + fun forceCollapsePanels_callsCentralSurfaces() { + val underTest = PanelInteractorImpl(Optional.of(centralSurfaces)) + + underTest.forceCollapsePanels() + + verify(centralSurfaces).postAnimateForceCollapsePanels() + } + + @Test + fun whenOptionalEmpty_doesnThrow() { + val underTest = PanelInteractorImpl(Optional.empty()) + + underTest.openPanels() + underTest.collapsePanels() + underTest.forceCollapsePanels() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt index 33921c7c84b1..3642e874e7ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt @@ -31,9 +31,12 @@ import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.LocationController +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before @@ -41,6 +44,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -65,6 +69,8 @@ class LocationTileTest : SysuiTestCase() { private lateinit var locationController: LocationController @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock + private lateinit var panelInteractor: PanelInteractor private val uiEventLogger = UiEventLoggerFake() private lateinit var testableLooper: TestableLooper @@ -86,7 +92,9 @@ class LocationTileTest : SysuiTestCase() { activityStarter, qsLogger, locationController, - keyguardStateController) + keyguardStateController, + panelInteractor, + ) } @After @@ -116,4 +124,18 @@ class LocationTileTest : SysuiTestCase() { assertThat(state.icon) .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_on)) } + + @Test + fun testClickWhenLockedWillCallOpenPanels() { + `when`(keyguardStateController.isMethodSecure).thenReturn(true) + `when`(keyguardStateController.isShowing).thenReturn(true) + + tile.handleClick(null) + + val captor = argumentCaptor<Runnable>() + verify(activityStarter).postQSRunnableDismissingKeyguard(capture(captor)) + captor.value.run() + + verify(panelInteractor).openPanels() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index 5aef75832fac..d9ed1a299f51 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -45,6 +45,7 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; @@ -83,6 +84,8 @@ public class ScreenRecordTileTest extends SysuiTestCase { private KeyguardStateController mKeyguardStateController; @Mock private DialogLaunchAnimator mDialogLaunchAnimator; + @Mock + private PanelInteractor mPanelInteractor; private TestableLooper mTestableLooper; private ScreenRecordTile mTile; @@ -108,7 +111,8 @@ public class ScreenRecordTileTest extends SysuiTestCase { mController, mKeyguardDismissUtil, mKeyguardStateController, - mDialogLaunchAnimator + mDialogLaunchAnimator, + mPanelInteractor ); mTile.initialize(); @@ -146,7 +150,7 @@ public class ScreenRecordTileTest extends SysuiTestCase { assertNotNull(onStartRecordingClicked.getValue()); onStartRecordingClicked.getValue().run(); verify(mDialogLaunchAnimator).disableAllCurrentDialogsExitAnimations(); - verify(mHost).collapsePanels(); + verify(mPanelInteractor).collapsePanels(); } // Test that the tile is active and labeled correctly when the controller is starting 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 15b84238dd19..d8ffe39e427d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java @@ -275,9 +275,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase { @Test public void testPanelStaysOpenWhenClosingQs() { - mShadeExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 1, - /* expanded= */ true, /* tracking= */ false, /* dragDownPxAmount= */ 0); - mQsController.setShadeExpandedHeight(1); + mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 1); float shadeExpandedHeight = mQsController.getShadeExpandedHeight(); mQsController.animateCloseQs(false); @@ -289,7 +287,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase { public void interceptTouchEvent_withinQs_shadeExpanded_startsQsTracking() { mQsController.setQs(mQs); - mQsController.setShadeExpandedHeight(1f); + mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 1); mQsController.onIntercept( createMotionEvent(0, 0, ACTION_DOWN)); mQsController.onIntercept( @@ -303,7 +301,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase { enableSplitShade(true); mQsController.setQs(mQs); - mQsController.setShadeExpandedHeight(1f); + mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 1); mQsController.onIntercept( createMotionEvent(0, 0, ACTION_DOWN)); mQsController.onIntercept( @@ -342,13 +340,8 @@ public class QuickSettingsControllerTest extends SysuiTestCase { public void handleTouch_downActionInQsArea() { mQsController.setQs(mQs); mQsController.setBarState(SHADE); - mQsController.onPanelExpansionChanged( - new ShadeExpansionChangeEvent( - 0.5f, - true, - true, - 0 - )); + mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 0.5f); + MotionEvent event = createMotionEvent(QS_FRAME_WIDTH / 2, QS_FRAME_BOTTOM / 2, ACTION_DOWN); mQsController.handleTouch(event, false, false); @@ -385,7 +378,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase { @Test public void handleTouch_isConflictingExpansionGestureSet() { assertThat(mQsController.isConflictingExpansionGesture()).isFalse(); - mShadeExpansionStateManager.onPanelExpansionChanged(1f, true, false, 0f); + mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 1); mQsController.handleTouch(MotionEvent.obtain(0L /* downTime */, 0L /* eventTime */, ACTION_DOWN, 0f /* x */, 0f /* y */, 0 /* metaState */), false, false); @@ -394,7 +387,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase { @Test public void handleTouch_isConflictingExpansionGestureSet_cancel() { - mShadeExpansionStateManager.onPanelExpansionChanged(1f, true, false, 0f); + mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 1); mQsController.handleTouch(createMotionEvent(0, 0, ACTION_DOWN), false, false); assertThat(mQsController.isConflictingExpansionGesture()).isTrue(); mQsController.handleTouch(createMotionEvent(0, 0, ACTION_UP), true, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index 1fdb3647fcb2..374aae1f2948 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -27,13 +27,16 @@ import com.android.systemui.plugins.ClockMetadata import com.android.systemui.plugins.ClockProviderPlugin import com.android.systemui.plugins.ClockSettings import com.android.systemui.plugins.PluginListener +import com.android.systemui.plugins.PluginLifecycleManager import com.android.systemui.plugins.PluginManager import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock import junit.framework.Assert.assertEquals import junit.framework.Assert.fail import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.TestScope import org.junit.Before import org.junit.Rule @@ -49,6 +52,7 @@ import org.mockito.junit.MockitoJUnit class ClockRegistryTest : SysuiTestCase() { @JvmField @Rule val mockito = MockitoJUnit.rule() + private lateinit var scheduler: TestCoroutineScheduler private lateinit var dispatcher: CoroutineDispatcher private lateinit var scope: TestScope @@ -58,37 +62,38 @@ class ClockRegistryTest : SysuiTestCase() { @Mock private lateinit var mockDefaultClock: ClockController @Mock private lateinit var mockThumbnail: Drawable @Mock private lateinit var mockContentResolver: ContentResolver + @Mock private lateinit var mockPluginLifecycle: PluginLifecycleManager<ClockProviderPlugin> private lateinit var fakeDefaultProvider: FakeClockPlugin private lateinit var pluginListener: PluginListener<ClockProviderPlugin> private lateinit var registry: ClockRegistry companion object { - private fun failFactory(): ClockController { - fail("Unexpected call to createClock") + private fun failFactory(clockId: ClockId): ClockController { + fail("Unexpected call to createClock: $clockId") return null!! } - private fun failThumbnail(): Drawable? { - fail("Unexpected call to getThumbnail") + private fun failThumbnail(clockId: ClockId): Drawable? { + fail("Unexpected call to getThumbnail: $clockId") return null } } private class FakeClockPlugin : ClockProviderPlugin { private val metadata = mutableListOf<ClockMetadata>() - private val createCallbacks = mutableMapOf<ClockId, () -> ClockController>() - private val thumbnailCallbacks = mutableMapOf<ClockId, () -> Drawable?>() + private val createCallbacks = mutableMapOf<ClockId, (ClockId) -> ClockController>() + private val thumbnailCallbacks = mutableMapOf<ClockId, (ClockId) -> Drawable?>() override fun getClocks() = metadata override fun createClock(settings: ClockSettings): ClockController = - createCallbacks[settings.clockId!!]!!() - override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!() + createCallbacks[settings.clockId!!]!!(settings.clockId!!) + override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!(id) fun addClock( id: ClockId, name: String, - create: () -> ClockController = ::failFactory, - getThumbnail: () -> Drawable? = ::failThumbnail + create: (ClockId) -> ClockController = ::failFactory, + getThumbnail: (ClockId) -> Drawable? = ::failThumbnail ): FakeClockPlugin { metadata.add(ClockMetadata(id, name)) createCallbacks[id] = create @@ -99,7 +104,8 @@ class ClockRegistryTest : SysuiTestCase() { @Before fun setUp() { - dispatcher = StandardTestDispatcher() + scheduler = TestCoroutineScheduler() + dispatcher = StandardTestDispatcher(scheduler) scope = TestScope(dispatcher) fakeDefaultProvider = FakeClockPlugin() @@ -116,6 +122,8 @@ class ClockRegistryTest : SysuiTestCase() { isEnabled = true, handleAllUsers = true, defaultClockProvider = fakeDefaultProvider, + keepAllLoaded = true, + subTag = "Test", ) { override fun querySettings() { } override fun applySettings(value: ClockSettings?) { @@ -142,8 +150,8 @@ class ClockRegistryTest : SysuiTestCase() { .addClock("clock_3", "clock 3") .addClock("clock_4", "clock 4") - pluginListener.onPluginConnected(plugin1, mockContext) - pluginListener.onPluginConnected(plugin2, mockContext) + pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle) + pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle) val list = registry.getClocks() assertEquals( list, @@ -165,16 +173,18 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun clockIdConflict_ErrorWithoutCrash() { + val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>() val plugin1 = FakeClockPlugin() .addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail }) .addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail }) + val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>() val plugin2 = FakeClockPlugin() .addClock("clock_1", "clock 1") .addClock("clock_2", "clock 2") - pluginListener.onPluginConnected(plugin1, mockContext) - pluginListener.onPluginConnected(plugin2, mockContext) + pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1) + pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2) val list = registry.getClocks() assertEquals( list, @@ -202,8 +212,8 @@ class ClockRegistryTest : SysuiTestCase() { .addClock("clock_4", "clock 4") registry.applySettings(ClockSettings("clock_3", null)) - pluginListener.onPluginConnected(plugin1, mockContext) - pluginListener.onPluginConnected(plugin2, mockContext) + pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle) + pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle) val clock = registry.createCurrentClock() assertEquals(mockClock, clock) @@ -220,9 +230,9 @@ class ClockRegistryTest : SysuiTestCase() { .addClock("clock_4", "clock 4") registry.applySettings(ClockSettings("clock_3", null)) - pluginListener.onPluginConnected(plugin1, mockContext) - pluginListener.onPluginConnected(plugin2, mockContext) - pluginListener.onPluginDisconnected(plugin2) + pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle) + pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle) + pluginListener.onPluginUnloaded(plugin2, mockPluginLifecycle) val clock = registry.createCurrentClock() assertEquals(clock, mockDefaultClock) @@ -230,15 +240,16 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun pluginRemoved_clockAndListChanged() { + val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>() val plugin1 = FakeClockPlugin() .addClock("clock_1", "clock 1") .addClock("clock_2", "clock 2") + val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>() val plugin2 = FakeClockPlugin() .addClock("clock_3", "clock 3", { mockClock }) .addClock("clock_4", "clock 4") - var changeCallCount = 0 var listChangeCallCount = 0 registry.registerClockChangeListener(object : ClockRegistry.ClockChangeListener { @@ -247,23 +258,38 @@ class ClockRegistryTest : SysuiTestCase() { }) registry.applySettings(ClockSettings("clock_3", null)) - assertEquals(0, changeCallCount) + scheduler.runCurrent() + assertEquals(1, changeCallCount) assertEquals(0, listChangeCallCount) - pluginListener.onPluginConnected(plugin1, mockContext) - assertEquals(0, changeCallCount) + pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1) + scheduler.runCurrent() + assertEquals(1, changeCallCount) assertEquals(1, listChangeCallCount) - pluginListener.onPluginConnected(plugin2, mockContext) - assertEquals(1, changeCallCount) + pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2) + scheduler.runCurrent() + assertEquals(2, changeCallCount) assertEquals(2, listChangeCallCount) - pluginListener.onPluginDisconnected(plugin1) - assertEquals(1, changeCallCount) + pluginListener.onPluginUnloaded(plugin1, mockPluginLifecycle1) + scheduler.runCurrent() + assertEquals(2, changeCallCount) + assertEquals(2, listChangeCallCount) + + pluginListener.onPluginUnloaded(plugin2, mockPluginLifecycle2) + scheduler.runCurrent() + assertEquals(3, changeCallCount) + assertEquals(2, listChangeCallCount) + + pluginListener.onPluginDetached(mockPluginLifecycle1) + scheduler.runCurrent() + assertEquals(3, changeCallCount) assertEquals(3, listChangeCallCount) - pluginListener.onPluginDisconnected(plugin2) - assertEquals(2, changeCallCount) + pluginListener.onPluginDetached(mockPluginLifecycle2) + scheduler.runCurrent() + assertEquals(3, changeCallCount) assertEquals(4, listChangeCallCount) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java index 05280fa826ed..c39b29fb4435 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java @@ -79,11 +79,11 @@ public class PluginActionManagerTest extends SysuiTestCase { private PluginInstance<TestPlugin> mPluginInstance; private PluginInstance.Factory mPluginInstanceFactory = new PluginInstance.Factory( this.getClass().getClassLoader(), - new PluginInstance.InstanceFactory<>(), new PluginInstance.VersionChecker(), + new PluginInstance.InstanceFactory<>(), new PluginInstance.VersionCheckerImpl(), Collections.emptyList(), false) { @Override public <T extends Plugin> PluginInstance<T> create(Context context, ApplicationInfo appInfo, - ComponentName componentName, Class<T> pluginClass) { + ComponentName componentName, Class<T> pluginClass, PluginListener<T> listener) { return (PluginInstance<T>) mPluginInstance; } }; @@ -128,7 +128,7 @@ public class PluginActionManagerTest extends SysuiTestCase { createPlugin(); // Verify startup lifecycle - verify(mPluginInstance).onCreate(mContext, mMockListener); + verify(mPluginInstance).onCreate(); } @Test @@ -140,7 +140,7 @@ public class PluginActionManagerTest extends SysuiTestCase { mFakeExecutor.runAllReady(); // Verify shutdown lifecycle - verify(mPluginInstance).onDestroy(mMockListener); + verify(mPluginInstance).onDestroy(); } @Test @@ -152,9 +152,9 @@ public class PluginActionManagerTest extends SysuiTestCase { mFakeExecutor.runAllReady(); // Verify the old one was destroyed. - verify(mPluginInstance).onDestroy(mMockListener); + verify(mPluginInstance).onDestroy(); verify(mPluginInstance, Mockito.times(2)) - .onCreate(mContext, mMockListener); + .onCreate(); } @Test @@ -188,7 +188,7 @@ public class PluginActionManagerTest extends SysuiTestCase { mFakeExecutor.runAllReady(); // Verify startup lifecycle - verify(mPluginInstance).onCreate(mContext, mMockListener); + verify(mPluginInstance).onCreate(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java index bb9a1e971fd0..d5e904c636d5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java @@ -16,11 +16,9 @@ package com.android.systemui.shared.plugins; +import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static junit.framework.Assert.assertNull; import android.content.ComponentName; import android.content.Context; @@ -31,6 +29,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginLifecycleManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.annotations.ProvidesInterface; import com.android.systemui.plugins.annotations.Requires; @@ -38,46 +37,64 @@ import com.android.systemui.plugins.annotations.Requires; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import java.lang.ref.WeakReference; import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; @SmallTest @RunWith(AndroidJUnit4.class) public class PluginInstanceTest extends SysuiTestCase { private static final String PRIVILEGED_PACKAGE = "com.android.systemui.plugins"; + private static final ComponentName TEST_PLUGIN_COMPONENT_NAME = + new ComponentName(PRIVILEGED_PACKAGE, TestPluginImpl.class.getName()); - @Mock - private TestPluginImpl mMockPlugin; - @Mock - private PluginListener<TestPlugin> mMockListener; - @Mock + private FakeListener mPluginListener; private VersionInfo mVersionInfo; - ComponentName mTestPluginComponentName = - new ComponentName(PRIVILEGED_PACKAGE, TestPluginImpl.class.getName()); + private VersionInfo.InvalidVersionException mVersionException; + private PluginInstance.VersionChecker mVersionChecker; + + private RefCounter mCounter; private PluginInstance<TestPlugin> mPluginInstance; private PluginInstance.Factory mPluginInstanceFactory; - private ApplicationInfo mAppInfo; - private Context mPluginContext; - @Mock - private PluginInstance.VersionChecker mVersionChecker; + + // Because we're testing memory in this file, we must be careful not to assert the target + // objects, or capture them via mockito if we expect the garbage collector to later free them. + // Both JUnit and Mockito will save references and prevent these objects from being cleaned up. + private WeakReference<TestPluginImpl> mPlugin; + private WeakReference<Context> mPluginContext; @Before public void setup() throws Exception { - MockitoAnnotations.initMocks(this); + mCounter = new RefCounter(); mAppInfo = mContext.getApplicationInfo(); - mAppInfo.packageName = mTestPluginComponentName.getPackageName(); - when(mVersionChecker.checkVersion(any(), any(), any())).thenReturn(mVersionInfo); + mAppInfo.packageName = TEST_PLUGIN_COMPONENT_NAME.getPackageName(); + mPluginListener = new FakeListener(); + mVersionInfo = new VersionInfo(); + mVersionChecker = new PluginInstance.VersionChecker() { + @Override + public <T extends Plugin> VersionInfo checkVersion( + Class<T> instanceClass, + Class<T> pluginClass, + Plugin plugin + ) { + if (mVersionException != null) { + throw mVersionException; + } + return mVersionInfo; + } + }; mPluginInstanceFactory = new PluginInstance.Factory( this.getClass().getClassLoader(), new PluginInstance.InstanceFactory<TestPlugin>() { @Override TestPlugin create(Class cls) { - return mMockPlugin; + TestPluginImpl plugin = new TestPluginImpl(mCounter); + mPlugin = new WeakReference<>(plugin); + return plugin; } }, mVersionChecker, @@ -85,8 +102,9 @@ public class PluginInstanceTest extends SysuiTestCase { false); mPluginInstance = mPluginInstanceFactory.create( - mContext, mAppInfo, mTestPluginComponentName, TestPlugin.class); - mPluginContext = mPluginInstance.getPluginContext(); + mContext, mAppInfo, TEST_PLUGIN_COMPONENT_NAME, + TestPlugin.class, mPluginListener); + mPluginContext = new WeakReference<>(mPluginInstance.getPluginContext()); } @Test @@ -96,29 +114,51 @@ public class PluginInstanceTest extends SysuiTestCase { @Test(expected = VersionInfo.InvalidVersionException.class) public void testIncorrectVersion() throws Exception { - ComponentName wrongVersionTestPluginComponentName = new ComponentName(PRIVILEGED_PACKAGE, TestPlugin.class.getName()); - when(mVersionChecker.checkVersion(any(), any(), any())).thenThrow( - new VersionInfo.InvalidVersionException("test", true)); + mVersionException = new VersionInfo.InvalidVersionException("test", true); mPluginInstanceFactory.create( - mContext, mAppInfo, wrongVersionTestPluginComponentName, TestPlugin.class); + mContext, mAppInfo, wrongVersionTestPluginComponentName, + TestPlugin.class, mPluginListener); } @Test public void testOnCreate() { - mPluginInstance.onCreate(mContext, mMockListener); - verify(mMockPlugin).onCreate(mContext, mPluginContext); - verify(mMockListener).onPluginConnected(mMockPlugin, mPluginContext); + mPluginInstance.onCreate(); + assertEquals(1, mPluginListener.mAttachedCount); + assertEquals(1, mPluginListener.mLoadCount); + assertEquals(mPlugin.get(), mPluginInstance.getPlugin()); + assertInstances(1, 1); } @Test public void testOnDestroy() { - mPluginInstance.onDestroy(mMockListener); - verify(mMockListener).onPluginDisconnected(mMockPlugin); - verify(mMockPlugin).onDestroy(); + mPluginInstance.onDestroy(); + assertEquals(1, mPluginListener.mDetachedCount); + assertEquals(1, mPluginListener.mUnloadCount); + assertNull(mPluginInstance.getPlugin()); + assertInstances(0, -1); // Destroyed but never created + } + + @Test + public void testOnRepeatedlyLoadUnload_PluginFreed() { + mPluginInstance.onCreate(); + mPluginInstance.loadPlugin(); + assertInstances(1, 1); + + mPluginInstance.unloadPlugin(); + assertNull(mPluginInstance.getPlugin()); + assertInstances(0, 0); + + mPluginInstance.loadPlugin(); + assertInstances(1, 1); + + mPluginInstance.unloadPlugin(); + mPluginInstance.onDestroy(); + assertNull(mPluginInstance.getPlugin()); + assertInstances(0, 0); } // This target class doesn't matter, it just needs to have a Requires to hit the flow where @@ -129,10 +169,103 @@ public class PluginInstanceTest extends SysuiTestCase { String ACTION = "testAction"; } + public void assertInstances(Integer allocated, Integer created) { + // Run the garbage collector to finalize and deallocate outstanding + // instances. Since the GC doesn't always appear to want to run + // completely when we ask, we ask it 10 times in a short loop. + for (int i = 0; i < 10; i++) { + System.runFinalization(); + System.gc(); + } + + mCounter.assertInstances(allocated, created); + } + + public static class RefCounter { + public final AtomicInteger mAllocatedInstances = new AtomicInteger(); + public final AtomicInteger mCreatedInstances = new AtomicInteger(); + + public void assertInstances(Integer allocated, Integer created) { + if (allocated != null) { + assertEquals(allocated.intValue(), mAllocatedInstances.get()); + } + if (created != null) { + assertEquals(created.intValue(), mCreatedInstances.get()); + } + } + } + @Requires(target = TestPlugin.class, version = TestPlugin.VERSION) public static class TestPluginImpl implements TestPlugin { + public final RefCounter mCounter; + public TestPluginImpl(RefCounter counter) { + mCounter = counter; + mCounter.mAllocatedInstances.getAndIncrement(); + } + + @Override + public void finalize() { + mCounter.mAllocatedInstances.getAndDecrement(); + } + @Override public void onCreate(Context sysuiContext, Context pluginContext) { + mCounter.mCreatedInstances.getAndIncrement(); + } + + @Override + public void onDestroy() { + mCounter.mCreatedInstances.getAndDecrement(); + } + } + + public class FakeListener implements PluginListener<TestPlugin> { + public int mAttachedCount = 0; + public int mDetachedCount = 0; + public int mLoadCount = 0; + public int mUnloadCount = 0; + + @Override + public void onPluginAttached(PluginLifecycleManager<TestPlugin> manager) { + mAttachedCount++; + assertEquals(PluginInstanceTest.this.mPluginInstance, manager); + } + + @Override + public void onPluginDetached(PluginLifecycleManager<TestPlugin> manager) { + mDetachedCount++; + assertEquals(PluginInstanceTest.this.mPluginInstance, manager); + } + + @Override + public void onPluginLoaded( + TestPlugin plugin, + Context pluginContext, + PluginLifecycleManager<TestPlugin> manager + ) { + mLoadCount++; + TestPlugin expectedPlugin = PluginInstanceTest.this.mPlugin.get(); + if (expectedPlugin != null) { + assertEquals(expectedPlugin, plugin); + } + Context expectedContext = PluginInstanceTest.this.mPluginContext.get(); + if (expectedContext != null) { + assertEquals(expectedContext, pluginContext); + } + assertEquals(PluginInstanceTest.this.mPluginInstance, manager); + } + + @Override + public void onPluginUnloaded( + TestPlugin plugin, + PluginLifecycleManager<TestPlugin> manager + ) { + mUnloadCount++; + TestPlugin expectedPlugin = PluginInstanceTest.this.mPlugin.get(); + if (expectedPlugin != null) { + assertEquals(expectedPlugin, plugin); + } + assertEquals(PluginInstanceTest.this.mPluginInstance, manager); } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt index c92134b2212b..60bc3a45c0dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt @@ -21,6 +21,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger +import com.android.internal.statusbar.IStatusBarService import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FeatureFlags @@ -93,6 +94,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() { private val bubblesManager: BubblesManager = mock() private val dragController: ExpandableNotificationRowDragController = mock() private val dismissibilityProvider: NotificationDismissibilityProvider = mock() + private val statusBarService: IStatusBarService = mock() private lateinit var controller: ExpandableNotificationRowController @@ -130,7 +132,8 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() { peopleNotificationIdentifier, Optional.of(bubblesManager), dragController, - dismissibilityProvider + dismissibilityProvider, + statusBarService ) whenever(view.childrenContainer).thenReturn(childrenContainer) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt index 7b2051da4d15..0b90ebec3ec6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt @@ -74,7 +74,7 @@ class NotificationContentViewTest : SysuiTestCase() { doReturn(10).whenever(spyRow).intrinsicHeight with(view) { - initialize(mPeopleNotificationIdentifier, mock(), mock(), mock()) + initialize(mPeopleNotificationIdentifier, mock(), mock(), mock(), mock()) setContainingNotification(spyRow) setHeights(/* smallHeight= */ 10, /* headsUpMaxHeight= */ 20, /* maxHeight= */ 30) contractedChild = createViewWithHeight(10) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index f8a8e50efb4c..813bae893569 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -49,6 +49,7 @@ import android.view.LayoutInflater; import android.widget.RemoteViews; import com.android.internal.logging.MetricsLogger; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.TestableDependency; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; @@ -582,7 +583,8 @@ public class NotificationTestHelper { mock(MetricsLogger.class), mock(SmartReplyConstants.class), mock(SmartReplyController.class), - mFeatureFlags); + mFeatureFlags, + mock(IStatusBarService.class)); row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 7a1270f3521d..a9ed17531926 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -25,6 +25,7 @@ import static com.android.systemui.statusbar.phone.ScrimState.SHADE_LOCKED; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; @@ -1163,8 +1164,8 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testScrimFocus() { mScrimController.transitionTo(ScrimState.AOD); - Assert.assertFalse("Should not be focusable on AOD", mScrimBehind.isFocusable()); - Assert.assertFalse("Should not be focusable on AOD", mScrimInFront.isFocusable()); + assertFalse("Should not be focusable on AOD", mScrimBehind.isFocusable()); + assertFalse("Should not be focusable on AOD", mScrimInFront.isFocusable()); mScrimController.transitionTo(ScrimState.KEYGUARD); Assert.assertTrue("Should be focusable on keyguard", mScrimBehind.isFocusable()); @@ -1224,7 +1225,7 @@ public class ScrimControllerTest extends SysuiTestCase { public void testAnimatesTransitionToAod() { when(mDozeParameters.shouldControlScreenOff()).thenReturn(false); ScrimState.AOD.prepare(ScrimState.KEYGUARD); - Assert.assertFalse("No animation when ColorFade kicks in", + assertFalse("No animation when ColorFade kicks in", ScrimState.AOD.getAnimateChange()); reset(mDozeParameters); @@ -1236,9 +1237,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testViewsDontHaveFocusHighlight() { - Assert.assertFalse("Scrim shouldn't have focus highlight", + assertFalse("Scrim shouldn't have focus highlight", mScrimInFront.getDefaultFocusHighlightEnabled()); - Assert.assertFalse("Scrim shouldn't have focus highlight", + assertFalse("Scrim shouldn't have focus highlight", mScrimBehind.getDefaultFocusHighlightEnabled()); } @@ -1738,7 +1739,7 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void aodStateSetsFrontScrimToNotBlend() { mScrimController.transitionTo(ScrimState.AOD); - Assert.assertFalse("Front scrim should not blend with main color", + assertFalse("Front scrim should not blend with main color", mScrimInFront.shouldBlendWithMainColor()); } @@ -1773,6 +1774,14 @@ public class ScrimControllerTest extends SysuiTestCase { verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway(); } + @Test + public void testDoNotAnimateChangeIfOccludeAnimationPlaying() { + mScrimController.setOccludeAnimationPlaying(true); + mScrimController.transitionTo(ScrimState.UNLOCKED); + + assertFalse(ScrimState.UNLOCKED.mAnimateChange); + } + private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) { mScrimController.setRawPanelExpansionFraction(expansion); finishAnimationsImmediately(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java index 481d453fa0b1..c8f28bc17926 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java @@ -55,6 +55,9 @@ import org.mockito.MockitoAnnotations; public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase { private static final String[] DEFAULT_SETTINGS = new String[]{"0:1", "2:0:1", "1:2"}; + private static final int[] DEFAULT_FOLDED_STATES = new int[]{0}; + private static final int[] DEFAULT_HALF_FOLDED_STATES = new int[]{2}; + private static final int[] DEFAULT_UNFOLDED_STATES = new int[]{1}; @Mock private DeviceStateManager mDeviceStateManager; @Mock private DeviceStateRotationLockSettingControllerLogger mLogger; @@ -73,6 +76,9 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase MockitoAnnotations.initMocks(/* testClass= */ this); TestableResources resources = mContext.getOrCreateTestableResources(); resources.addOverride(R.array.config_perDeviceStateRotationLockDefaults, DEFAULT_SETTINGS); + resources.addOverride(R.array.config_foldedDeviceStates, DEFAULT_FOLDED_STATES); + resources.addOverride(R.array.config_halfFoldedDeviceStates, DEFAULT_HALF_FOLDED_STATES); + resources.addOverride(R.array.config_openDeviceStates, DEFAULT_UNFOLDED_STATES); ArgumentCaptor<DeviceStateManager.DeviceStateCallback> deviceStateCallbackArgumentCaptor = ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 8e3988b2c038..ee4e00baafe6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -1245,6 +1245,24 @@ public class BubblesTest extends SysuiTestCase { // Show the menu stackView.showManageMenu(true); assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); + assertTrue(stackView.isManageMenuSettingsVisible()); + } + + @Test + public void testShowManageMenuChangesSysuiState_appBubble() { + mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0); + assertTrue(mBubbleController.hasBubbles()); + + // Expand the stack + BubbleStackView stackView = mBubbleController.getStackView(); + mBubbleData.setExpanded(true); + assertStackExpanded(); + assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + + // Show the menu + stackView.showManageMenu(true); + assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); + assertFalse(stackView.isManageMenuSettingsVisible()); } @Test diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 755019684da6..b4e75e193b65 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9351,7 +9351,9 @@ public class ActivityManagerService extends IActivityManager.Stub String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag; String maxBytesSetting = Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag; - int lines = Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0); + int lines = Build.IS_USER + ? 0 + : Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0); int dropboxMaxSize = Settings.Global.getInt( mContext.getContentResolver(), maxBytesSetting, DROPBOX_DEFAULT_MAX_SIZE); int maxDataFileSize = dropboxMaxSize - sb.length() diff --git a/services/core/java/com/android/server/biometrics/TEST_MAPPING b/services/core/java/com/android/server/biometrics/TEST_MAPPING index 8b80674070a4..daca00b40768 100644 --- a/services/core/java/com/android/server/biometrics/TEST_MAPPING +++ b/services/core/java/com/android/server/biometrics/TEST_MAPPING @@ -6,5 +6,24 @@ { "name": "CtsBiometricsHostTestCases" } - ] -}
\ No newline at end of file + ], + "ironwood-postsubmit": [ + { + "name": "BiometricsE2eTests", + "options": [ + { + "include-annotation": "android.platform.test.annotations.IwTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.platform.test.scenario.biometrics" + }, + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } + ] +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index dc00ffc9f922..01ffc7e29ac0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -314,7 +314,8 @@ public class FingerprintService extends SystemService { final FingerprintSensorPropertiesInternal sensorProps = provider.second.getSensorProperties(options.getSensorId()); if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName) - && sensorProps != null && sensorProps.isAnyUdfpsType()) { + && sensorProps != null && (sensorProps.isAnyUdfpsType() + || sensorProps.isAnySidefpsType())) { try { return authenticateWithPrompt(operationId, sensorProps, callingUid, callingUserId, receiver, opPackageName, diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java index 77cd67304729..a081dff9e62d 100644 --- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java +++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java @@ -92,6 +92,8 @@ public class GnssConfiguration { // Represents an HAL interface version. Instances of this class are created in the JNI layer // and returned through native methods. static class HalInterfaceVersion { + // mMajor being this value denotes AIDL HAL. In this case, mMinor denotes the AIDL version. + static final int AIDL_INTERFACE = 3; final int mMajor; final int mMinor; diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java index 6c4c829b051d..041f11d972fe 100644 --- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java @@ -21,6 +21,7 @@ import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; import static com.android.server.location.gnss.GnssManagerService.D; import static com.android.server.location.gnss.GnssManagerService.TAG; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.location.GnssMeasurementRequest; @@ -31,6 +32,7 @@ import android.os.IBinder; import android.stats.location.LocationStatsEnums; import android.util.Log; +import com.android.server.location.gnss.GnssConfiguration.HalInterfaceVersion; import com.android.server.location.gnss.hal.GnssNative; import com.android.server.location.injector.AppOpsHelper; import com.android.server.location.injector.Injector; @@ -115,16 +117,6 @@ public final class GnssMeasurementsProvider extends if (request.getIntervalMillis() == GnssMeasurementRequest.PASSIVE_INTERVAL) { return true; } - // The HAL doc does not specify if consecutive start() calls will be allowed. - // Some vendors may ignore the 2nd start() call if stop() is not called. - // Thus, here we always call stop() before calling start() to avoid being ignored. - if (mGnssNative.stopMeasurementCollection()) { - if (D) { - Log.d(TAG, "stopping gnss measurements"); - } - } else { - Log.e(TAG, "error stopping gnss measurements"); - } if (mGnssNative.startMeasurementCollection(request.isFullTracking(), request.isCorrelationVectorOutputsEnabled(), request.getIntervalMillis())) { @@ -139,6 +131,28 @@ public final class GnssMeasurementsProvider extends } @Override + protected boolean reregisterWithService(GnssMeasurementRequest old, + GnssMeasurementRequest request, + @NonNull Collection<GnssListenerRegistration> registrations) { + if (request.getIntervalMillis() == GnssMeasurementRequest.PASSIVE_INTERVAL) { + unregisterWithService(); + return true; + } + HalInterfaceVersion halInterfaceVersion = + mGnssNative.getConfiguration().getHalInterfaceVersion(); + boolean aidlV3Plus = halInterfaceVersion.mMajor == HalInterfaceVersion.AIDL_INTERFACE + && halInterfaceVersion.mMinor >= 3; + if (!aidlV3Plus) { + // The HAL doc does not specify if consecutive start() calls will be allowed. + // Some vendors may ignore the 2nd start() call if stop() is not called. + // Thus, here we always call stop() before calling start() to avoid being ignored. + // AIDL v3+ is free from this issue. + unregisterWithService(); + } + return registerWithService(request, registrations); + } + + @Override protected void unregisterWithService() { if (mGnssNative.stopMeasurementCollection()) { if (D) { diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index d160740712d3..5312ae6ca84c 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -69,7 +69,6 @@ final class ReconcilePackageUtils { for (InstallRequest installRequest : installRequests) { installRequest.onReconcileStarted(); - final String installPackageName = installRequest.getParsedPackage().getPackageName(); // add / replace existing with incoming packages combinedPackages.put(installRequest.getScannedPackageSetting().getPackageName(), @@ -84,12 +83,17 @@ final class ReconcilePackageUtils { incomingSharedLibraries, info)) { throw ReconcileFailure.ofInternalError( "Shared Library " + info.getName() - + " is being installed twice in this set!", + + " is being installed twice in this set!", PackageManagerException.INTERNAL_ERROR_SHARED_LIB_INSTALLED_TWICE); } } } + } + for (InstallRequest installRequest : installRequests) { + final String installPackageName = installRequest.getParsedPackage().getPackageName(); + final List<SharedLibraryInfo> allowedSharedLibInfos = + sharedLibraries.getAllowedSharedLibInfos(installRequest); final DeletePackageAction deletePackageAction; // we only want to try to delete for non system apps diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 324a0adee693..e21c15665140 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4357,17 +4357,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override void addWindow(WindowState w) { super.addWindow(w); - - boolean gotReplacementWindow = false; - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState candidate = mChildren.get(i); - gotReplacementWindow |= candidate.setReplacementWindowIfNeeded(w); - } - - // if we got a replacement window, reset the timeout to give drawing more time - if (gotReplacementWindow) { - mWmService.scheduleWindowReplacementTimeouts(this); - } checkKeyguardFlagsChanged(); } @@ -4382,12 +4371,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A updateLetterboxSurface(child); } - void onWindowReplacementTimeout() { - for (int i = mChildren.size() - 1; i >= 0; --i) { - (mChildren.get(i)).onWindowReplacementTimeout(); - } - } - void setAppLayoutChanges(int changes, String reason) { if (!mChildren.isEmpty()) { final DisplayContent dc = getDisplayContent(); @@ -4398,15 +4381,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - void removeReplacedWindowIfNeeded(WindowState replacement) { - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState win = mChildren.get(i); - if (win.removeReplacedWindowIfNeeded(replacement)) { - return; - } - } - } - private boolean transferStartingWindow(@NonNull ActivityRecord fromActivity) { final WindowState tStartingWindow = fromActivity.mStartingWindow; if (tStartingWindow != null && fromActivity.mStartingSurface != null) { @@ -5633,10 +5607,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // * activity is transitioning visibility state // * or the activity was marked as hidden and is exiting before we had a chance to play the // transition animation - // * or this is an opening app and windows are being replaced (e.g. freeform window to - // normal window). - return isVisible() != visible || mRequestForceTransition || (!isVisible() && mIsExiting) - || (visible && forAllWindows(WindowState::waitingForReplacement, true)); + return isVisible() != visible || mRequestForceTransition || (!isVisible() && mIsExiting); } /** @@ -7380,35 +7351,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - void setWillReplaceWindows(boolean animate) { - ProtoLog.d(WM_DEBUG_ADD_REMOVE, - "Marking app token %s with replacing windows.", this); - - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState w = mChildren.get(i); - w.setWillReplaceWindow(animate); - } - } - - void setWillReplaceChildWindows() { - ProtoLog.d(WM_DEBUG_ADD_REMOVE, "Marking app token %s" - + " with replacing child windows.", this); - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState w = mChildren.get(i); - w.setWillReplaceChildWindows(); - } - } - - void clearWillReplaceWindows() { - ProtoLog.d(WM_DEBUG_ADD_REMOVE, - "Resetting app token %s of replacing window marks.", this); - - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState w = mChildren.get(i); - w.clearWillReplaceWindow(); - } - } - void requestUpdateWallpaperIfNeeded() { for (int i = mChildren.size() - 1; i >= 0; i--) { final WindowState w = mChildren.get(i); diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 0b28ba2bb146..bc9efc88ecae 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -377,15 +377,10 @@ class DragState { mDragWindowHandle.ownerUid = MY_UID; mDragWindowHandle.scaleFactor = 1.0f; - // InputConfig.PREVENT_SPLITTING: To keep the default behavior of this window to be - // focusable, which allows the system to consume keys when dragging is active. This can - // also be used to modify the drag state on key press. For example, cancel drag on - // escape key. // InputConfig.TRUSTED_OVERLAY: To not block any touches while D&D ongoing and allowing // touches to pass through to windows underneath. This allows user to interact with the // UI to navigate while dragging. - mDragWindowHandle.inputConfig = - InputConfig.PREVENT_SPLITTING | InputConfig.TRUSTED_OVERLAY; + mDragWindowHandle.inputConfig = InputConfig.TRUSTED_OVERLAY; // The drag window cannot receive new touches. mDragWindowHandle.touchableRegion.setEmpty(); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index b2a4df1f4692..fda2125be3cf 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -44,7 +44,6 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER; import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; -import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS; import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; @@ -432,13 +431,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } }; - private static final Consumer<WindowState> sRemoveReplacedWindowsConsumer = w -> { - final ActivityRecord activity = w.mActivityRecord; - if (activity != null) { - activity.removeReplacedWindowIfNeeded(w); - } - }; - RootWindowContainer(WindowManagerService service) { super(service); mHandler = new MyHandler(service.mH.getLooper()); @@ -662,17 +654,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> forAllWindows(mCloseSystemDialogsConsumer, false /* traverseTopToBottom */); } - void removeReplacedWindows() { - ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION removeReplacedWindows"); - mWmService.openSurfaceTransaction(); - try { - forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */); - } finally { - mWmService.closeSurfaceTransaction("removeReplacedWindows"); - ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION removeReplacedWindows"); - } - } - boolean hasPendingLayoutChanges(WindowAnimator animator) { boolean hasChanges = false; diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index ce9bff8521e6..78ee6f95fdba 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -232,11 +232,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override - public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) { - mService.setWillReplaceWindows(appToken, childrenOnly); - } - - @Override public boolean cancelDraw(IWindow window) { return mService.cancelDraw(this, window); } @@ -862,7 +857,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type, - IBinder windowToken, IBinder focusGrantToken, String inputHandleName, + int inputFeatures, IBinder windowToken, IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) { if (hostInputToken == null && !mCanAddInternalSystemWindow) { // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to @@ -874,7 +869,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { try { mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken, flags, mCanAddInternalSystemWindow ? privateFlags : 0, - type, windowToken, focusGrantToken, inputHandleName, + type, inputFeatures, windowToken, focusGrantToken, inputHandleName, outInputChannel); } finally { Binder.restoreCallingIdentity(identity); @@ -883,11 +878,11 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags, int privateFlags, Region region) { + int flags, int privateFlags, int inputFeatures, Region region) { final long identity = Binder.clearCallingIdentity(); try { mService.updateInputChannel(channelToken, displayId, surface, flags, - mCanAddInternalSystemWindow ? privateFlags : 0, region); + mCanAddInternalSystemWindow ? privateFlags : 0, inputFeatures, region); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 67ca8443102b..6882e4c1d557 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -866,23 +866,8 @@ class Task extends TaskFragment { return false; } - final int toRootTaskWindowingMode = toRootTask.getWindowingMode(); final ActivityRecord topActivity = getTopNonFinishingActivity(); - final boolean mightReplaceWindow = topActivity != null - && replaceWindowsOnTaskMove(getWindowingMode(), toRootTaskWindowingMode); - if (mightReplaceWindow) { - // We are about to relaunch the activity because its configuration changed due to - // being maximized, i.e. size change. The activity will first remove the old window - // and then add a new one. This call will tell window manager about this, so it can - // preserve the old window until the new one is drawn. This prevents having a gap - // between the removal and addition, in which no window is visible. We also want the - // entrance of the new window to be properly animated. - // Note here we always set the replacing window first, as the flags might be needed - // during the relaunch. If we end up not doing any relaunch, we clear the flags later. - windowManager.setWillReplaceWindow(topActivity.token, animate); - } - mAtmService.deferWindowLayout(); boolean kept = true; try { @@ -926,17 +911,10 @@ class Task extends TaskFragment { mAtmService.continueWindowLayout(); } - if (mightReplaceWindow) { - // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old - // window), we need to clear the replace window settings. Otherwise, we schedule a - // timeout to remove the old window if the replacing window is not coming in time. - windowManager.scheduleClearWillReplaceWindows(topActivity.token, !kept); - } - if (!deferResume) { // The task might have already been running and its visibility needs to be synchronized // with the visibility of the root task / windows. - root.ensureActivitiesVisible(null, 0, !mightReplaceWindow); + root.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); root.resumeFocusedTasksTopActivities(); } @@ -947,17 +925,6 @@ class Task extends TaskFragment { return (preferredRootTask == toRootTask); } - /** - * @return {@code true} if the windows of tasks being moved to the target root task from the - * source root task should be replaced, meaning that window manager will keep the old window - * around until the new is ready. - */ - private static boolean replaceWindowsOnTaskMove( - int sourceWindowingMode, int targetWindowingMode) { - return sourceWindowingMode == WINDOWING_MODE_FREEFORM - || targetWindowingMode == WINDOWING_MODE_FREEFORM; - } - void touchActiveTime() { lastActiveTime = SystemClock.elapsedRealtime(); } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 2b848d57e2f9..0b9ceeaf5d4e 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -163,14 +163,6 @@ class WallpaperController { if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen() + " mDrawState=" + w.mWinAnimator.mDrawState); - if (w.mWillReplaceWindow && mWallpaperTarget == null - && !mFindResults.useTopWallpaperAsTarget) { - // When we are replacing a window and there was wallpaper before replacement, we want to - // keep the window until the new windows fully appear and can determine the visibility, - // to avoid flickering. - mFindResults.setUseTopWallpaperAsTarget(true); - } - final WindowContainer animatingContainer = w.mActivityRecord != null ? w.mActivityRecord.getAnimatingContainer() : null; final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index c11391e1236e..25965331241e 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -69,10 +69,6 @@ public class WindowAnimator { SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators = new SparseArray<>(2); private boolean mInitialized = false; - // When set to true the animator will go over all windows after an animation frame is posted and - // check if some got replaced and can be removed. - private boolean mRemoveReplacedWindows = false; - private Choreographer mChoreographer; /** @@ -217,11 +213,6 @@ public class WindowAnimator { mService.closeSurfaceTransaction("WindowAnimator"); ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate"); - if (mRemoveReplacedWindows) { - root.removeReplacedWindows(); - mRemoveReplacedWindows = false; - } - mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); executeAfterPrepareSurfacesRunnables(); @@ -286,10 +277,6 @@ public class WindowAnimator { return displayAnimator; } - void requestRemovalOfReplacedWindows(WindowState win) { - mRemoveReplacedWindows = true; - } - void scheduleAnimation() { if (!mAnimationFrameCallbackScheduled) { mAnimationFrameCallbackScheduled = true; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 42d23e755b21..be42f36f7d30 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -88,7 +88,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManager.TRANSIT_RELAUNCH; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.fixScale; import static android.view.WindowManagerGlobal.ADD_OKAY; @@ -382,9 +381,6 @@ public class WindowManagerService extends IWindowManager.Stub /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */ static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000; - /** Amount of time (in milliseconds) to delay before declaring a window replacement timeout. */ - static final int WINDOW_REPLACEMENT_TIMEOUT_DURATION = 2000; - /** Amount of time to allow a last ANR message to exist before freeing the memory. */ static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours @@ -565,12 +561,6 @@ public class WindowManagerService extends IWindowManager.Stub final WindowManagerGlobalLock mGlobalLock; /** - * List of app window tokens that are waiting for replacing windows. If the - * replacement doesn't come in time the stale windows needs to be disposed of. - */ - final ArrayList<ActivityRecord> mWindowReplacementTimeouts = new ArrayList<>(); - - /** * Windows that are being resized. Used so we can tell the client about * the resize after closing the transaction in which we resized the * underlying surface. @@ -1774,15 +1764,6 @@ public class WindowManagerService extends IWindowManager.Stub final WindowStateAnimator winAnimator = win.mWinAnimator; winAnimator.mEnterAnimationPending = true; winAnimator.mEnteringAnimation = true; - // Check if we need to prepare a transition for replacing window first. - if (!win.mTransitionController.isShellTransitionsEnabled() - && activity != null && activity.isVisible() - && !prepareWindowReplacementTransition(activity)) { - // If not, check if need to set up a dummy transition during display freeze - // so that the unfreeze wait for the apps to draw. This might be needed if - // the app is relaunching. - prepareNoneTransitionForRelaunching(activity); - } if (displayPolicy.areSystemBarsForcedConsumedLw()) { res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS; @@ -1944,48 +1925,6 @@ public class WindowManagerService extends IWindowManager.Stub } /** - * Returns true if we're done setting up any transitions. - */ - private boolean prepareWindowReplacementTransition(ActivityRecord activity) { - activity.clearAllDrawn(); - final WindowState replacedWindow = activity.getReplacingWindow(); - if (replacedWindow == null) { - // We expect to already receive a request to remove the old window. If it did not - // happen, let's just simply add a window. - return false; - } - // We use the visible frame, because we want the animation to morph the window from what - // was visible to the user to the final destination of the new window. - final Rect frame = new Rect(replacedWindow.getFrame()); - final WindowManager.LayoutParams attrs = replacedWindow.mAttrs; - frame.inset(replacedWindow.getInsetsStateWithVisibilityOverride().calculateVisibleInsets( - frame, attrs.type, replacedWindow.getWindowingMode(), attrs.softInputMode, - attrs.flags)); - // We treat this as if this activity was opening, so we can trigger the app transition - // animation and piggy-back on existing transition animation infrastructure. - final DisplayContent dc = activity.getDisplayContent(); - dc.mOpeningApps.add(activity); - dc.prepareAppTransition(TRANSIT_RELAUNCH); - dc.mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top, - frame.width(), frame.height()); - dc.executeAppTransition(); - return true; - } - - private void prepareNoneTransitionForRelaunching(ActivityRecord activity) { - // Set up a none-transition and add the app to opening apps, so that the display - // unfreeze wait for the apps to be drawn. - // Note that if the display unfroze already because app unfreeze timed out, - // we don't set up the transition anymore and just let it go. - final DisplayContent dc = activity.getDisplayContent(); - if (mDisplayFrozen && !dc.mOpeningApps.contains(activity) && activity.isRelaunching()) { - dc.mOpeningApps.add(activity); - dc.prepareAppTransition(TRANSIT_NONE); - dc.executeAppTransition(); - } - } - - /** * Set whether screen capture is disabled for all windows of a specific user from * the device policy cache. */ @@ -2390,12 +2329,6 @@ public class WindowManagerService extends IWindowManager.Stub // If we are not currently running the exit animation, we need to see about starting // one. - // We don't want to animate visibility of windows which are pending replacement. - // In the case of activity relaunch child windows could request visibility changes as - // they are detached from the main application window during the tear down process. - // If we satisfied these visibility changes though, we would cause a visual glitch - // hiding the window before it's replacement was available. So we just do nothing on - // our side. // This must be called before the call to performSurfacePlacement. if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) { if (DEBUG_VISIBILITY) { @@ -2403,20 +2336,18 @@ public class WindowManagerService extends IWindowManager.Stub "Relayout invis " + win + ": mAnimatingExit=" + win.mAnimatingExit); } result |= RELAYOUT_RES_SURFACE_CHANGED; - if (!win.mWillReplaceWindow) { - // When FLAG_SHOW_WALLPAPER flag is removed from a window, we usually set a flag - // in DC#pendingLayoutChanges and update the wallpaper target later. - // However it's possible that FLAG_SHOW_WALLPAPER flag is removed from a window - // when the window is about to exit, so we update the wallpaper target - // immediately here. Otherwise this window will be stuck in exiting and its - // surface remains on the screen. - // TODO(b/189856716): Allow destroying surface even if it belongs to the - // keyguard target. - if (wallpaperMayMove) { - displayContent.mWallpaperController.adjustWallpaperWindows(); - } - tryStartExitingAnimation(win, winAnimator); + // When FLAG_SHOW_WALLPAPER flag is removed from a window, we usually set a flag + // in DC#pendingLayoutChanges and update the wallpaper target later. + // However it's possible that FLAG_SHOW_WALLPAPER flag is removed from a window + // when the window is about to exit, so we update the wallpaper target + // immediately here. Otherwise this window will be stuck in exiting and its + // surface remains on the screen. + // TODO(b/189856716): Allow destroying surface even if it belongs to the + // keyguard target. + if (wallpaperMayMove) { + displayContent.mWallpaperController.adjustWallpaperWindows(); } + tryStartExitingAnimation(win, winAnimator); } // Create surfaceControl before surface placement otherwise layout will be skipped @@ -5328,8 +5259,6 @@ public class WindowManagerService extends IWindowManager.Stub public static final int UPDATE_MULTI_WINDOW_STACKS = 41; - public static final int WINDOW_REPLACEMENT_TIMEOUT = 46; - public static final int UPDATE_ANIMATION_SCALE = 51; public static final int WINDOW_HIDE_TIMEOUT = 52; public static final int RESTORE_POINTER_ICON = 55; @@ -5555,16 +5484,6 @@ public class WindowManagerService extends IWindowManager.Stub } break; } - case WINDOW_REPLACEMENT_TIMEOUT: { - synchronized (mGlobalLock) { - for (int i = mWindowReplacementTimeouts.size() - 1; i >= 0; i--) { - final ActivityRecord activity = mWindowReplacementTimeouts.get(i); - activity.onWindowReplacementTimeout(); - } - mWindowReplacementTimeouts.clear(); - } - break; - } case WINDOW_HIDE_TIMEOUT: { final WindowState window = (WindowState) msg.obj; synchronized (mGlobalLock) { @@ -7083,98 +7002,6 @@ public class WindowManagerService extends IWindowManager.Stub return mGlobalLock; } - /** - * Hint to a token that its activity will relaunch, which will trigger removal and addition of - * a window. - * - * @param token Application token for which the activity will be relaunched. - */ - void setWillReplaceWindow(IBinder token, boolean animate) { - final ActivityRecord activity = mRoot.getActivityRecord(token); - if (activity == null) { - ProtoLog.w(WM_ERROR, "Attempted to set replacing window on non-existing app token %s", - token); - return; - } - if (!activity.hasContentToDisplay()) { - ProtoLog.w(WM_ERROR, - "Attempted to set replacing window on app token with no content %s", - token); - return; - } - activity.setWillReplaceWindows(animate); - } - - /** - * Hint to a token that its windows will be replaced across activity relaunch. - * The windows would otherwise be removed shortly following this as the - * activity is torn down. - * @param token Application token for which the activity will be relaunched. - * @param childrenOnly Whether to mark only child windows for replacement - * (for the case where main windows are being preserved/ - * reused rather than replaced). - * - */ - // TODO: The s at the end of the method name is the only difference with the name of the method - // above. We should combine them or find better names. - void setWillReplaceWindows(IBinder token, boolean childrenOnly) { - synchronized (mGlobalLock) { - final ActivityRecord activity = mRoot.getActivityRecord(token); - if (activity == null) { - ProtoLog.w(WM_ERROR, - "Attempted to set replacing window on non-existing app token %s", - token); - return; - } - if (!activity.hasContentToDisplay()) { - ProtoLog.w(WM_ERROR, - "Attempted to set replacing window on app token with no content %s", - token); - return; - } - - if (childrenOnly) { - activity.setWillReplaceChildWindows(); - } else { - activity.setWillReplaceWindows(false /* animate */); - } - - scheduleClearWillReplaceWindows(token, true /* replacing */); - } - } - - /** - * If we're replacing the window, schedule a timer to clear the replaced window - * after a timeout, in case the replacing window is not coming. - * - * If we're not replacing the window, clear the replace window settings of the app. - * - * @param token Application token for the activity whose window might be replaced. - * @param replacing Whether the window is being replaced or not. - */ - void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) { - final ActivityRecord activity = mRoot.getActivityRecord(token); - if (activity == null) { - ProtoLog.w(WM_ERROR, "Attempted to reset replacing window on non-existing app token %s", - token); - return; - } - if (replacing) { - scheduleWindowReplacementTimeouts(activity); - } else { - activity.clearWillReplaceWindows(); - } - } - - void scheduleWindowReplacementTimeouts(ActivityRecord activity) { - if (!mWindowReplacementTimeouts.contains(activity)) { - mWindowReplacementTimeouts.add(activity); - } - mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT); - mH.sendEmptyMessageDelayed( - H.WINDOW_REPLACEMENT_TIMEOUT, WINDOW_REPLACEMENT_TIMEOUT_DURATION); - } - @Override public int getDockedStackSide() { return 0; @@ -8744,8 +8571,8 @@ public class WindowManagerService extends IWindowManager.Stub */ void grantInputChannel(Session session, int callingUid, int callingPid, int displayId, SurfaceControl surface, IWindow window, IBinder hostInputToken, - int flags, int privateFlags, int type, IBinder windowToken, IBinder focusGrantToken, - String inputHandleName, InputChannel outInputChannel) { + int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken, + IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) { final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type); final InputApplicationHandle applicationHandle; final String name; @@ -8762,7 +8589,7 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, - name, applicationHandle, flags, privateFlags, sanitizedType, + name, applicationHandle, flags, privateFlags, inputFeatures, sanitizedType, null /* region */, window); clientChannel.copyTo(outInputChannel); @@ -8803,13 +8630,14 @@ public class WindowManagerService extends IWindowManager.Stub private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid, int displayId, SurfaceControl surface, String name, InputApplicationHandle applicationHandle, int flags, - int privateFlags, int type, Region region, IWindow window) { + int privateFlags, int inputFeatures, int type, Region region, IWindow window) { final InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId); h.token = channelToken; h.setWindowToken(window); h.name = name; flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid); + inputFeatures = sanitizeSpyWindow(inputFeatures, name, callingUid, callingPid); final int sanitizedLpFlags = (flags & (FLAG_NOT_TOUCHABLE | FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE)) @@ -8819,7 +8647,7 @@ public class WindowManagerService extends IWindowManager.Stub // Do not allow any input features to be set without sanitizing them first. h.inputConfig = InputConfigAdapter.getInputConfigFromWindowParams( - type, sanitizedLpFlags, 0 /*inputFeatures*/); + type, sanitizedLpFlags, inputFeatures); if ((flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0) { @@ -8856,7 +8684,7 @@ public class WindowManagerService extends IWindowManager.Stub * is undefined. */ void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags, int privateFlags, Region region) { + int flags, int privateFlags, int inputFeatures, Region region) { final InputApplicationHandle applicationHandle; final String name; final EmbeddedWindowController.EmbeddedWindow win; @@ -8871,7 +8699,8 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name, - applicationHandle, flags, privateFlags, win.mWindowType, region, win.mClient); + applicationHandle, flags, privateFlags, inputFeatures, win.mWindowType, region, + win.mClient); } /** Return whether layer tracing is enabled */ diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index d6c03113e87f..8a083aa6220f 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -59,13 +59,11 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NOT_MAGNIFIAB import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -632,22 +630,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean mHasSurface = false; - // This window will be replaced due to relaunch. This allows window manager - // to differentiate between simple removal of a window and replacement. In the latter case it - // will preserve the old window until the new one is drawn. - boolean mWillReplaceWindow = false; - // If true, the replaced window was already requested to be removed. - private boolean mReplacingRemoveRequested = false; - // Whether the replacement of the window should trigger app transition animation. - private boolean mAnimateReplacingWindow = false; - // If not null, the window that will be used to replace the old one. This is being set when - // the window is added and unset when this window reports its first draw. - private WindowState mReplacementWindow = null; - // For the new window in the replacement transition, if we have - // requested to replace without animation, then we should - // make sure we also don't apply an enter animation for - // the new window. - boolean mSkipEnterAnimationForSeamlessReplacement = false; // Whether this window is being moved via the resize API private boolean mMovedByResize; @@ -1309,13 +1291,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } boolean skipLayout() { - if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) { - // This window is being replaced and either already got information that it's being - // removed or we are still waiting for some information. Because of this we don't - // want to apply any more changes to it, so it remains in this state until new window - // appears. - return true; - } // Skip layout of the window when in transition to pip mode. return mActivityRecord != null && mActivityRecord.mWaitForEnteringPinnedMode; } @@ -2354,24 +2329,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - void onWindowReplacementTimeout() { - if (mWillReplaceWindow) { - // Since the window already timed out, remove it immediately now. - // Use WindowState#removeImmediately() instead of WindowState#removeIfPossible(), as - // the latter delays removal on certain conditions, which will leave the stale window - // in the root task and marked mWillReplaceWindow=false, so the window will never be - // removed. - // - // Also removes child windows. - removeImmediately(); - } else { - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowState c = mChildren.get(i); - c.onWindowReplacementTimeout(); - } - } - } - @Override void removeImmediately() { if (mRemoved) { @@ -2388,11 +2345,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWinAnimator.destroySurfaceLocked(getSyncTransaction()); super.removeImmediately(); - mWillReplaceWindow = false; - if (mReplacementWindow != null) { - mReplacementWindow.mSkipEnterAnimationForSeamlessReplacement = false; - } - final DisplayContent dc = getDisplayContent(); if (isImeLayeringTarget()) { // Remove the attached IME screenshot surface. @@ -2472,12 +2424,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b " + "mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b " - + "mWillReplaceWindow=%b mDisplayFrozen=%b callers=%s", + + "mDisplayFrozen=%b callers=%s", this, mWinAnimator.mSurfaceController, mAnimatingExit, mRemoveOnExit, mHasSurface, mWinAnimator.getShown(), isAnimating(TRANSITION | PARENTS), mActivityRecord != null && mActivityRecord.isAnimating(PARENTS | TRANSITION), - mWillReplaceWindow, mWmService.mDisplayFrozen, Debug.getCallers(6)); // Visibility of the removed window. Will be used later to update orientation later on. @@ -2487,22 +2438,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // window until the animation is done. If the display is frozen, just remove immediately, // since the animation wouldn't be seen. if (mHasSurface && mToken.okToAnimate()) { - if (mWillReplaceWindow) { - // This window is going to be replaced. We need to keep it around until the new one - // gets added, then we will get rid of this one. - ProtoLog.v(WM_DEBUG_ADD_REMOVE, - "Preserving %s until the new one is added", this); - // TODO: We are overloading mAnimatingExit flag to prevent the window state from - // been removed. We probably need another flag to indicate that window removal - // should be deffered vs. overloading the flag that says we are playing an exit - // animation. - ProtoLog.v(WM_DEBUG_ANIM, - "Set animatingExit: reason=remove/replaceWindow win=%s", this); - mAnimatingExit = true; - mReplacingRemoveRequested = true; - return; - } - // If we are not currently running the exit animation, we need to see about starting one wasVisible = isVisible(); @@ -2714,53 +2649,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mInputWindowHandle.setToken(null); } - /** Returns true if the replacement window was removed. */ - boolean removeReplacedWindowIfNeeded(WindowState replacement) { - if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawn()) { - replacement.mSkipEnterAnimationForSeamlessReplacement = false; - removeReplacedWindow(); - return true; - } - - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowState c = mChildren.get(i); - if (c.removeReplacedWindowIfNeeded(replacement)) { - return true; - } - } - return false; - } - - private void removeReplacedWindow() { - ProtoLog.d(WM_DEBUG_ADD_REMOVE, "Removing replaced window: %s", this); - mWillReplaceWindow = false; - mAnimateReplacingWindow = false; - mReplacingRemoveRequested = false; - mReplacementWindow = null; - if (mAnimatingExit || !mAnimateReplacingWindow) { - removeImmediately(); - } - } - - boolean setReplacementWindowIfNeeded(WindowState replacementCandidate) { - boolean replacementSet = false; - - if (mWillReplaceWindow && mReplacementWindow == null - && getWindowTag().toString().equals(replacementCandidate.getWindowTag().toString())) { - - mReplacementWindow = replacementCandidate; - replacementCandidate.mSkipEnterAnimationForSeamlessReplacement = !mAnimateReplacingWindow; - replacementSet = true; - } - - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowState c = mChildren.get(i); - replacementSet |= c.setReplacementWindowIfNeeded(replacementCandidate); - } - - return replacementSet; - } - void setDisplayLayoutNeeded() { final DisplayContent dc = getDisplayContent(); if (dc != null) { @@ -4391,49 +4279,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return parent != null && parent.isGoneForLayout(); } - void setWillReplaceWindow(boolean animate) { - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState c = mChildren.get(i); - c.setWillReplaceWindow(animate); - } - - if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) != 0 - || mAttrs.type == TYPE_APPLICATION_STARTING) { - // We don't set replacing on starting windows since they are added by window manager and - // not the client so won't be replaced by the client. - return; - } - - mWillReplaceWindow = true; - mReplacementWindow = null; - mAnimateReplacingWindow = animate; - } - - void clearWillReplaceWindow() { - mWillReplaceWindow = false; - mReplacementWindow = null; - mAnimateReplacingWindow = false; - - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState c = mChildren.get(i); - c.clearWillReplaceWindow(); - } - } - - boolean waitingForReplacement() { - if (mWillReplaceWindow) { - return true; - } - - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState c = mChildren.get(i); - if (c.waitingForReplacement()) { - return true; - } - } - return false; - } - void requestUpdateWallpaperIfNeeded() { final DisplayContent dc = getDisplayContent(); if (dc != null && ((mIsWallpaper && !mLastConfigReportedToClient) || hasWallpaper())) { @@ -4464,43 +4309,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return winY; } - // During activity relaunch due to resize, we sometimes use window replacement - // for only child windows (as the main window is handled by window preservation) - // and the big surface. - // - // Though windows of TYPE_APPLICATION or TYPE_DRAWN_APPLICATION (as opposed to - // TYPE_BASE_APPLICATION) are not children in the sense of an attached window, - // we also want to replace them at such phases, as they won't be covered by window - // preservation, and in general we expect them to return following relaunch. - boolean shouldBeReplacedWithChildren() { - return mIsChildWindow || mAttrs.type == TYPE_APPLICATION - || mAttrs.type == TYPE_DRAWN_APPLICATION; - } - - void setWillReplaceChildWindows() { - if (shouldBeReplacedWithChildren()) { - setWillReplaceWindow(false /* animate */); - } - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState c = mChildren.get(i); - c.setWillReplaceChildWindows(); - } - } - - WindowState getReplacingWindow() { - if (mAnimatingExit && mWillReplaceWindow && mAnimateReplacingWindow) { - return this; - } - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState c = mChildren.get(i); - final WindowState replacing = c.getReplacingWindow(); - if (replacing != null) { - return replacing; - } - } - return null; - } - int getRotationAnimationHint() { if (mActivityRecord != null) { return mActivityRecord.mRotationAnimationHint; @@ -4990,14 +4798,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean clearAnimatingFlags() { boolean didSomething = false; - // We don't want to clear it out for windows that get replaced, because the - // animation depends on the flag to remove the replaced window. - // // We also don't clear the mAnimatingExit flag for windows which have the // mRemoveOnExit flag. This indicates an explicit remove request has been issued // by the client. We should let animation proceed and not clear this flag or // they won't eventually be removed by WindowStateAnimator#finishExit. - if (!mWillReplaceWindow && !mRemoveOnExit) { + if (!mRemoveOnExit) { // Clear mAnimating flag together with mAnimatingExit. When animation // changes from exiting to entering, we need to clear this flag until the // new animation gets applied, so that isAnimationStarting() becomes true @@ -5312,7 +5117,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return activity.needsZBoost(); } } - return mWillReplaceWindow; + return false; } private boolean isStartingWindowAssociatedToTask() { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index e8625bc3d64b..3aac816fcd7a 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -449,7 +449,6 @@ class WindowStateAnimator { if (prepared && mDrawState == HAS_DRAWN) { if (mLastHidden) { mSurfaceController.showRobustly(t); - mAnimator.requestRemovalOfReplacedWindows(w); mLastHidden = false; final DisplayContent displayContent = w.getDisplayContent(); if (!displayContent.getLastHasContent()) { @@ -504,13 +503,6 @@ class WindowStateAnimator { } void applyEnterAnimationLocked() { - // If we are the new part of a window replacement transition and we have requested - // not to animate, we instead want to make it seamless, so we don't want to apply - // an enter transition. - if (mWin.mSkipEnterAnimationForSeamlessReplacement) { - return; - } - final int transit; if (mEnterAnimationPending) { mEnterAnimationPending = false; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index c6b78982169c..327483ebbef7 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -315,17 +315,6 @@ class WindowToken extends WindowContainer<WindowState> { return mChildren.isEmpty(); } - WindowState getReplacingWindow() { - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState win = mChildren.get(i); - final WindowState replacing = win.getReplacingWindow(); - if (replacing != null) { - return replacing; - } - } - return null; - } - /** Return true if this token has a window that wants the wallpaper displayed behind it. */ boolean windowsCanBeWallpaperTarget() { for (int j = mChildren.size() - 1; j >= 0; j--) { diff --git a/services/core/jni/gnss/GnssConfiguration.cpp b/services/core/jni/gnss/GnssConfiguration.cpp index 3677641127f3..b57e451264a4 100644 --- a/services/core/jni/gnss/GnssConfiguration.cpp +++ b/services/core/jni/gnss/GnssConfiguration.cpp @@ -67,7 +67,7 @@ GnssConfiguration::GnssConfiguration(const sp<IGnssConfiguration>& iGnssConfigur : mIGnssConfiguration(iGnssConfiguration) {} jobject GnssConfiguration::getVersion(JNIEnv* env) { - return createHalInterfaceVersionJavaObject(env, 3, 0); + return createHalInterfaceVersionJavaObject(env, 3, mIGnssConfiguration->getInterfaceVersion()); } jboolean GnssConfiguration::setEmergencySuplPdn(jint enable) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index a1789b2b125d..321924c79c58 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -95,6 +95,7 @@ import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDG import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE; import static android.app.admin.DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY; import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE; +import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_FINANCING_STATE_CHANGED; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED; import static android.app.admin.DevicePolicyManager.ACTION_MANAGED_PROFILE_PROVISIONED; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; @@ -15440,7 +15441,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slogf.i(LOG_TAG, "Sending %s broadcast to manifest receivers.", intent.getAction()); broadcastIntentToCrossProfileManifestReceivers( intent, parentHandle, requiresPermission); - broadcastIntentToDevicePolicyManagerRoleHolder(intent, parentHandle); + broadcastExplicitIntentToRoleHolder( + intent, RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, parentHandle); } @Override @@ -15481,36 +15483,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void broadcastIntentToDevicePolicyManagerRoleHolder( - Intent intent, UserHandle userHandle) { - final int userId = userHandle.getIdentifier(); - final String packageName = getDevicePolicyManagementRoleHolderPackageName(mContext); - if (packageName == null) { - return; - } - try { - final Intent packageIntent = new Intent(intent) - .setPackage(packageName); - final List<ResolveInfo> receivers = mIPackageManager.queryIntentReceivers( - packageIntent, - /* resolvedType= */ null, - STOCK_PM_FLAGS, - userId).getList(); - if (receivers.isEmpty()) { - return; - } - for (ResolveInfo receiver : receivers) { - final Intent componentIntent = new Intent(packageIntent) - .setComponent(receiver.getComponentInfo().getComponentName()) - .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - mContext.sendBroadcastAsUser(componentIntent, userHandle); - } - } catch (RemoteException ex) { - Slogf.w(LOG_TAG, "Cannot get list of broadcast receivers for %s because: %s.", - intent.getAction(), ex); - } - } - /** * Checks whether the package {@code packageName} has the {@code MODIFY_QUIET_MODE} * permission granted for the user {@code userId}. @@ -20718,7 +20690,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void maybeInstallDevicePolicyManagementRoleHolderInUser(int targetUserId) { String devicePolicyManagerRoleHolderPackageName = - getDevicePolicyManagementRoleHolderPackageName(mContext); + getRoleHolderPackageName(mContext, RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT); if (devicePolicyManagerRoleHolderPackageName == null) { Slogf.d(LOG_TAG, "No device policy management role holder specified."); return; @@ -20744,14 +20716,24 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + /** + * If multiple packages hold the role, returns the first package in the list. + */ + @Nullable + private String getRoleHolderPackageName(Context context, String role) { + return getRoleHolderPackageNameOnUser(context, role, Process.myUserHandle()); + } - private String getDevicePolicyManagementRoleHolderPackageName(Context context) { + /** + * If multiple packages hold the role, returns the first package in the list. + */ + @Nullable + private String getRoleHolderPackageNameOnUser(Context context, String role, UserHandle user) { RoleManager roleManager = context.getSystemService(RoleManager.class); // Calling identity needs to be cleared as this method is used in the permissions checks. return mInjector.binderWithCleanCallingIdentity(() -> { - List<String> roleHolders = - roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT); + List<String> roleHolders = roleManager.getRoleHoldersAsUser(role, user); if (roleHolders.isEmpty()) { return null; } @@ -20762,7 +20744,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private boolean isCallerDevicePolicyManagementRoleHolder(CallerIdentity caller) { int callerUid = caller.getUid(); String devicePolicyManagementRoleHolderPackageName = - getDevicePolicyManagementRoleHolderPackageName(mContext); + getRoleHolderPackageName(mContext, RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT); int roleHolderUid = mInjector.getPackageManagerInternal().getPackageUid( devicePolicyManagementRoleHolderPackageName, 0, caller.getUserId()); @@ -21830,15 +21812,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } public void register() { - mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.SYSTEM); + mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL); } @Override public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { - if (!RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT.equals(roleName)) { + if (RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT.equals(roleName)) { + handleDevicePolicyManagementRoleChange(user); + return; + } + if (RoleManager.ROLE_FINANCED_DEVICE_KIOSK.equals(roleName)) { + handleFinancedDeviceKioskRoleChange(); return; } - String newRoleHolder = getRoleHolder(); + } + + private void handleDevicePolicyManagementRoleChange(UserHandle user) { + String newRoleHolder = getDeviceManagementRoleHolder(user); if (isDefaultRoleHolder(newRoleHolder)) { Slogf.i(LOG_TAG, "onRoleHoldersChanged: Default role holder is set, returning early"); @@ -21873,9 +21863,44 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private String getRoleHolder() { - return DevicePolicyManagerService.this.getDevicePolicyManagementRoleHolderPackageName( - mContext); + private void handleFinancedDeviceKioskRoleChange() { + if (!isDevicePolicyEngineEnabled()) { + return; + } + Slog.i(LOG_TAG, "Handling action " + ACTION_DEVICE_FINANCING_STATE_CHANGED); + Intent intent = new Intent(ACTION_DEVICE_FINANCING_STATE_CHANGED); + mInjector.binderWithCleanCallingIdentity(() -> { + for (UserInfo userInfo : mUserManager.getUsers()) { + UserHandle user = userInfo.getUserHandle(); + broadcastExplicitIntentToRoleHolder( + intent, RoleManager.ROLE_SYSTEM_SUPERVISION, user); + broadcastExplicitIntentToRoleHolder( + intent, RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, user); + ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(user.getIdentifier()); + if (admin == null) { + continue; + } + if (!isProfileOwnerOfOrganizationOwnedDevice( + admin.info.getComponent(), user.getIdentifier()) + && !isDeviceOwner(admin) + && !(isProfileOwner(admin.info.getComponent(), user.getIdentifier()) + && admin.getUserHandle().isSystem())) { + continue; + } + // Don't send the broadcast twice if the DPC is the same package as the + // DMRH + if (admin.info.getPackageName().equals(getDeviceManagementRoleHolder(user))) { + continue; + } + broadcastExplicitIntentToPackage( + intent, admin.info.getPackageName(), admin.getUserHandle()); + } + }); + } + + private String getDeviceManagementRoleHolder(UserHandle user) { + return DevicePolicyManagerService.this.getRoleHolderPackageNameOnUser( + mContext, RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, user); } private boolean isDefaultRoleHolder(String packageName) { @@ -21935,6 +21960,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + private void broadcastExplicitIntentToRoleHolder( + Intent intent, String role, UserHandle userHandle) { + String packageName = getRoleHolderPackageNameOnUser(mContext, role, userHandle); + if (packageName == null) { + return; + } + broadcastExplicitIntentToPackage(intent, packageName, userHandle); + } + + private void broadcastExplicitIntentToPackage( + Intent intent, String packageName, UserHandle userHandle) { + int userId = userHandle.getIdentifier(); + if (packageName == null) { + return; + } + Intent packageIntent = new Intent(intent) + .setPackage(packageName); + List<ResolveInfo> receivers = mContext.getPackageManager().queryBroadcastReceiversAsUser( + packageIntent, + PackageManager.ResolveInfoFlags.of(PackageManager.GET_RECEIVERS), + userId); + if (receivers.isEmpty()) { + Slog.i(LOG_TAG, "Found no receivers to handle intent " + intent + + " in package " + packageName); + return; + } + for (ResolveInfo receiver : receivers) { + Intent componentIntent = new Intent(packageIntent) + .setComponent(receiver.getComponentInfo().getComponentName()) + .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + mContext.sendBroadcastAsUser(componentIntent, userHandle); + } + } + @Override public List<UserHandle> getPolicyManagedProfiles(@NonNull UserHandle user) { Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java index 1ced95b92aa4..51dcc0323a96 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java @@ -148,6 +148,8 @@ public final class DisplayPowerController2Test { mCdsiMock).when(() -> LocalServices.getService( ColorDisplayService.ColorDisplayServiceInternal.class)); doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService); + doAnswer((Answer<Boolean>) invocationOnMock -> true).when(() -> + Settings.System.putFloatForUser(any(), any(), anyFloat(), anyInt())); setUpSensors(); mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); @@ -287,73 +289,6 @@ public final class DisplayPowerController2Test { eq(mProxSensor), anyInt(), any(Handler.class)); } - /** - * Creates a mock and registers it to {@link LocalServices}. - */ - private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { - LocalServices.removeServiceForTest(clazz); - LocalServices.addService(clazz, mock); - } - - private void advanceTime(long timeMs) { - mClock.fastForward(timeMs); - mTestLooper.dispatchAll(); - } - - private void setUpSensors() throws Exception { - mProxSensor = TestUtils.createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, - PROX_SENSOR_MAX_RANGE); - Sensor screenOffBrightnessSensor = TestUtils.createSensor( - Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); - when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL))) - .thenReturn(List.of(mProxSensor, screenOffBrightnessSensor)); - } - - private SensorEventListener getSensorEventListener(Sensor sensor) { - verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(), - eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class)); - return mSensorEventListenerCaptor.getValue(); - } - - private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock, - DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock, - boolean isEnabled) { - DisplayInfo info = new DisplayInfo(); - DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); - deviceInfo.uniqueId = uniqueId; - - when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); - when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); - when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); - when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); - when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); - when(logicalDisplayMock.getBrightnessThrottlingDataIdLocked()).thenReturn( - DisplayDeviceConfig.DEFAULT_ID); - when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); - when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); - when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); - when(displayDeviceConfigMock.getProximitySensor()).thenReturn( - new DisplayDeviceConfig.SensorData() { - { - type = Sensor.STRING_TYPE_PROXIMITY; - name = null; - } - }); - when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); - when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true); - when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn( - new DisplayDeviceConfig.SensorData()); - when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn( - new DisplayDeviceConfig.SensorData() { - { - type = Sensor.STRING_TYPE_LIGHT; - name = null; - } - }); - when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux()) - .thenReturn(new int[0]); - } - @Test public void testDisplayBrightnessFollowers_BothDpcsSupportNits() { DisplayPowerControllerHolder followerDpc = @@ -482,6 +417,32 @@ public final class DisplayPowerController2Test { verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); } + @Test + public void testDisplayBrightnessFollowers_AutomaticBrightness() { + doAnswer((Answer<Integer>) invocationOnMock -> + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) + .when(() -> Settings.System.getIntForUser(any(ContentResolver.class), + eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(), + eq(UserHandle.USER_CURRENT))); + final float brightness = 0.4f; + final float nits = 300; + final float ambientLux = 3000; + when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness()) + .thenReturn(brightness); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness()) + .thenReturn(0.3f); + when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); + when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + DisplayPowerController followerDpc = mock(DisplayPowerController.class); + + mHolder.dpc.addDisplayBrightnessFollower(followerDpc); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(followerDpc).setBrightnessToFollow(brightness, nits, ambientLux); + } @Test public void testDisplayBrightnessFollowersRemoval() { @@ -750,6 +711,73 @@ public final class DisplayPowerController2Test { verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat()); } + /** + * Creates a mock and registers it to {@link LocalServices}. + */ + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + + private void advanceTime(long timeMs) { + mClock.fastForward(timeMs); + mTestLooper.dispatchAll(); + } + + private void setUpSensors() throws Exception { + mProxSensor = TestUtils.createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, + PROX_SENSOR_MAX_RANGE); + Sensor screenOffBrightnessSensor = TestUtils.createSensor( + Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); + when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL))) + .thenReturn(List.of(mProxSensor, screenOffBrightnessSensor)); + } + + private SensorEventListener getSensorEventListener(Sensor sensor) { + verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(), + eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class)); + return mSensorEventListenerCaptor.getValue(); + } + + private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock, + DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock, + boolean isEnabled) { + DisplayInfo info = new DisplayInfo(); + DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); + deviceInfo.uniqueId = uniqueId; + + when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); + when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); + when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); + when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); + when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); + when(logicalDisplayMock.getBrightnessThrottlingDataIdLocked()).thenReturn( + DisplayDeviceConfig.DEFAULT_ID); + when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); + when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); + when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); + when(displayDeviceConfigMock.getProximitySensor()).thenReturn( + new DisplayDeviceConfig.SensorData() { + { + type = Sensor.STRING_TYPE_PROXIMITY; + name = null; + } + }); + when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); + when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true); + when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn( + new DisplayDeviceConfig.SensorData()); + when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn( + new DisplayDeviceConfig.SensorData() { + { + type = Sensor.STRING_TYPE_LIGHT; + name = null; + } + }); + when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux()) + .thenReturn(new int[0]); + } + private DisplayPowerControllerHolder createDisplayPowerController(int displayId, String uniqueId) { return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java index 53fcdad30ed2..0a1bf1c9ed99 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -149,6 +149,8 @@ public final class DisplayPowerControllerTest { mCdsiMock).when(() -> LocalServices.getService( ColorDisplayService.ColorDisplayServiceInternal.class)); doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService); + doAnswer((Answer<Boolean>) invocationOnMock -> true).when(() -> + Settings.System.putFloatForUser(any(), any(), anyFloat(), anyInt())); setUpSensors(); mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); @@ -290,73 +292,6 @@ public final class DisplayPowerControllerTest { eq(mProxSensor), anyInt(), any(Handler.class)); } - /** - * Creates a mock and registers it to {@link LocalServices}. - */ - private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { - LocalServices.removeServiceForTest(clazz); - LocalServices.addService(clazz, mock); - } - - private void advanceTime(long timeMs) { - mClock.fastForward(timeMs); - mTestLooper.dispatchAll(); - } - - private void setUpSensors() throws Exception { - mProxSensor = TestUtils.createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, - PROX_SENSOR_MAX_RANGE); - Sensor screenOffBrightnessSensor = TestUtils.createSensor( - Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); - when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL))) - .thenReturn(List.of(mProxSensor, screenOffBrightnessSensor)); - } - - private SensorEventListener getSensorEventListener(Sensor sensor) { - verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(), - eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class)); - return mSensorEventListenerCaptor.getValue(); - } - - private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock, - DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock, - boolean isEnabled) { - DisplayInfo info = new DisplayInfo(); - DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); - deviceInfo.uniqueId = uniqueId; - - when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); - when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); - when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); - when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); - when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); - when(logicalDisplayMock.getBrightnessThrottlingDataIdLocked()).thenReturn( - DisplayDeviceConfig.DEFAULT_ID); - when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); - when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); - when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); - when(displayDeviceConfigMock.getProximitySensor()).thenReturn( - new DisplayDeviceConfig.SensorData() { - { - type = Sensor.STRING_TYPE_PROXIMITY; - name = null; - } - }); - when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); - when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true); - when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn( - new DisplayDeviceConfig.SensorData()); - when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn( - new DisplayDeviceConfig.SensorData() { - { - type = Sensor.STRING_TYPE_LIGHT; - name = null; - } - }); - when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux()) - .thenReturn(new int[0]); - } - @Test public void testDisplayBrightnessFollowers_BothDpcsSupportNits() { DisplayPowerControllerHolder followerDpc = @@ -486,6 +421,33 @@ public final class DisplayPowerControllerTest { } @Test + public void testDisplayBrightnessFollowers_AutomaticBrightness() { + doAnswer((Answer<Integer>) invocationOnMock -> + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) + .when(() -> Settings.System.getIntForUser(any(ContentResolver.class), + eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(), + eq(UserHandle.USER_CURRENT))); + final float brightness = 0.4f; + final float nits = 300; + final float ambientLux = 3000; + when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness()) + .thenReturn(brightness); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness()) + .thenReturn(0.3f); + when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); + when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + DisplayPowerController followerDpc = mock(DisplayPowerController.class); + + mHolder.dpc.addDisplayBrightnessFollower(followerDpc); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(followerDpc).setBrightnessToFollow(brightness, nits, ambientLux); + } + + @Test public void testDisplayBrightnessFollowersRemoval() { DisplayPowerControllerHolder followerHolder = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID); @@ -517,7 +479,6 @@ public final class DisplayPowerControllerTest { verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), anyFloat()); - mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc); mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc); clearInvocations(followerHolder.animator); @@ -754,6 +715,73 @@ public final class DisplayPowerControllerTest { verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat()); } + /** + * Creates a mock and registers it to {@link LocalServices}. + */ + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + + private void advanceTime(long timeMs) { + mClock.fastForward(timeMs); + mTestLooper.dispatchAll(); + } + + private void setUpSensors() throws Exception { + mProxSensor = TestUtils.createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, + PROX_SENSOR_MAX_RANGE); + Sensor screenOffBrightnessSensor = TestUtils.createSensor( + Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); + when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL))) + .thenReturn(List.of(mProxSensor, screenOffBrightnessSensor)); + } + + private SensorEventListener getSensorEventListener(Sensor sensor) { + verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(), + eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class)); + return mSensorEventListenerCaptor.getValue(); + } + + private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock, + DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock, + boolean isEnabled) { + DisplayInfo info = new DisplayInfo(); + DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); + deviceInfo.uniqueId = uniqueId; + + when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); + when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); + when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); + when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); + when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); + when(logicalDisplayMock.getBrightnessThrottlingDataIdLocked()).thenReturn( + DisplayDeviceConfig.DEFAULT_ID); + when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); + when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); + when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); + when(displayDeviceConfigMock.getProximitySensor()).thenReturn( + new DisplayDeviceConfig.SensorData() { + { + type = Sensor.STRING_TYPE_PROXIMITY; + name = null; + } + }); + when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); + when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true); + when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn( + new DisplayDeviceConfig.SensorData()); + when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn( + new DisplayDeviceConfig.SensorData() { + { + type = Sensor.STRING_TYPE_LIGHT; + name = null; + } + }); + when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux()) + .thenReturn(new int[0]); + } + private DisplayPowerControllerHolder createDisplayPowerController(int displayId, String uniqueId) { return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java new file mode 100644 index 000000000000..fd9dfe869d52 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java @@ -0,0 +1,188 @@ +/* + * 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.location.gnss; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.location.GnssMeasurementRequest; +import android.location.IGnssMeasurementsListener; +import android.location.LocationManager; +import android.location.LocationManagerInternal; +import android.location.util.identity.CallerIdentity; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.LocalServices; +import com.android.server.location.gnss.hal.FakeGnssHal; +import com.android.server.location.gnss.hal.GnssNative; +import com.android.server.location.injector.FakeUserInfoHelper; +import com.android.server.location.injector.Injector; +import com.android.server.location.injector.TestInjector; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Objects; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class GnssMeasurementsProviderTest { + private static final int CURRENT_USER = FakeUserInfoHelper.DEFAULT_USERID; + private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1000, + "mypackage", "attribution", "listener"); + private static final GnssConfiguration.HalInterfaceVersion HIDL_V2_1 = + new GnssConfiguration.HalInterfaceVersion( + 2, 1); + private static final GnssConfiguration.HalInterfaceVersion AIDL_V3 = + new GnssConfiguration.HalInterfaceVersion( + GnssConfiguration.HalInterfaceVersion.AIDL_INTERFACE, 3); + private static final GnssMeasurementRequest ACTIVE_REQUEST = + new GnssMeasurementRequest.Builder().build(); + private static final GnssMeasurementRequest PASSIVE_REQUEST = + new GnssMeasurementRequest.Builder().setIntervalMillis( + GnssMeasurementRequest.PASSIVE_INTERVAL).build(); + private @Mock Context mContext; + private @Mock LocationManagerInternal mInternal; + private @Mock GnssConfiguration mMockConfiguration; + private @Mock GnssNative.GeofenceCallbacks mGeofenceCallbacks; + private @Mock IGnssMeasurementsListener mListener1; + private @Mock IGnssMeasurementsListener mListener2; + private @Mock IBinder mBinder1; + private @Mock IBinder mBinder2; + + private GnssNative mGnssNative; + + private GnssMeasurementsProvider mTestProvider; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + doReturn(mBinder1).when(mListener1).asBinder(); + doReturn(mBinder2).when(mListener2).asBinder(); + doReturn(true).when(mInternal).isProviderEnabledForUser(eq(LocationManager.GPS_PROVIDER), + anyInt()); + LocalServices.addService(LocationManagerInternal.class, mInternal); + FakeGnssHal fakeGnssHal = new FakeGnssHal(); + GnssNative.setGnssHalForTest(fakeGnssHal); + Injector injector = new TestInjector(mContext); + mGnssNative = spy(Objects.requireNonNull( + GnssNative.create(injector, mMockConfiguration))); + mGnssNative.setGeofenceCallbacks(mGeofenceCallbacks); + mTestProvider = new GnssMeasurementsProvider(injector, mGnssNative); + mGnssNative.register(); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(LocationManagerInternal.class); + } + + @Test + public void testAddListener_active() { + // add the active request + mTestProvider.addListener(ACTIVE_REQUEST, IDENTITY, mListener1); + verify(mGnssNative, times(1)).startMeasurementCollection( + eq(ACTIVE_REQUEST.isFullTracking()), + eq(ACTIVE_REQUEST.isCorrelationVectorOutputsEnabled()), + eq(ACTIVE_REQUEST.getIntervalMillis())); + + // remove the active request + mTestProvider.removeListener(mListener1); + verify(mGnssNative, times(1)).stopMeasurementCollection(); + } + + @Test + public void testAddListener_passive() { + // add the passive request + mTestProvider.addListener(PASSIVE_REQUEST, IDENTITY, mListener1); + verify(mGnssNative, never()).startMeasurementCollection(anyBoolean(), anyBoolean(), + anyInt()); + + // remove the passive request + mTestProvider.removeListener(mListener1); + verify(mGnssNative, times(1)).stopMeasurementCollection(); + } + + @Test + public void testReregister_aidlV3Plus() { + doReturn(AIDL_V3).when(mMockConfiguration).getHalInterfaceVersion(); + + // add the passive request + mTestProvider.addListener(PASSIVE_REQUEST, IDENTITY, mListener1); + verify(mGnssNative, never()).startMeasurementCollection(anyBoolean(), anyBoolean(), + anyInt()); + + // add the active request, reregister with the active request + mTestProvider.addListener(ACTIVE_REQUEST, IDENTITY, mListener2); + verify(mGnssNative, never()).stopMeasurementCollection(); + verify(mGnssNative, times(1)).startMeasurementCollection( + eq(ACTIVE_REQUEST.isFullTracking()), + eq(ACTIVE_REQUEST.isCorrelationVectorOutputsEnabled()), + eq(ACTIVE_REQUEST.getIntervalMillis())); + + // remove the active request, reregister with the passive request + mTestProvider.removeListener(mListener2); + verify(mGnssNative, times(1)).stopMeasurementCollection(); + + // remove the passive request + mTestProvider.removeListener(mListener1); + verify(mGnssNative, times(2)).stopMeasurementCollection(); + } + + @Test + public void testReregister_preAidlV3() { + doReturn(HIDL_V2_1).when(mMockConfiguration).getHalInterfaceVersion(); + + // add the passive request + mTestProvider.addListener(PASSIVE_REQUEST, IDENTITY, mListener1); + verify(mGnssNative, never()).startMeasurementCollection(anyBoolean(), anyBoolean(), + anyInt()); + + // add the active request, reregister with the active request + mTestProvider.addListener(ACTIVE_REQUEST, IDENTITY, mListener2); + verify(mGnssNative, times(1)).stopMeasurementCollection(); + verify(mGnssNative, times(1)).startMeasurementCollection( + eq(ACTIVE_REQUEST.isFullTracking()), + eq(ACTIVE_REQUEST.isCorrelationVectorOutputsEnabled()), + eq(ACTIVE_REQUEST.getIntervalMillis())); + + // remove the active request, reregister with the passive request + mTestProvider.removeListener(mListener2); + verify(mGnssNative, times(2)).stopMeasurementCollection(); + + // remove the passive request + mTestProvider.removeListener(mListener1); + verify(mGnssNative, times(3)).stopMeasurementCollection(); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index c73237efa93e..0ae579bab413 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -376,46 +376,6 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test - public void testGetAnimationTargets_windowsAreBeingReplaced() { - // [DisplayContent] -+- [Task1] - [ActivityRecord1] (opening, visible) - // +- [AppWindow1] (being-replaced) - // +- [Task2] - [ActivityRecord2] (closing, invisible) - // +- [AppWindow2] (being-replaced) - final ActivityRecord activity1 = createActivityRecord(mDisplayContent); - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( - TYPE_BASE_APPLICATION); - attrs.setTitle("AppWindow1"); - final TestWindowState appWindow1 = createWindowState(attrs, activity1); - appWindow1.mWillReplaceWindow = true; - activity1.addWindow(appWindow1); - - final ActivityRecord activity2 = createActivityRecord(mDisplayContent); - activity2.setVisible(false); - activity2.setVisibleRequested(false); - attrs.setTitle("AppWindow2"); - final TestWindowState appWindow2 = createWindowState(attrs, activity2); - appWindow2.mWillReplaceWindow = true; - activity2.addWindow(appWindow2); - - final ArraySet<ActivityRecord> opening = new ArraySet<>(); - opening.add(activity1); - final ArraySet<ActivityRecord> closing = new ArraySet<>(); - closing.add(activity2); - - // Animate opening apps even if it's already visible in case its windows are being replaced. - // Don't animate closing apps if it's already invisible even though its windows are being - // replaced. - assertEquals( - new ArraySet<>(new WindowContainer[]{activity1.getRootTask()}), - AppTransitionController.getAnimationTargets( - opening, closing, true /* visible */)); - assertEquals( - new ArraySet<>(new WindowContainer[]{}), - AppTransitionController.getAnimationTargets( - opening, closing, false /* visible */)); - } - - @Test public void testGetAnimationTargets_openingClosingInDifferentTask() { // [DisplayContent] -+- [Task1] -+- [ActivityRecord1] (opening, invisible) // | +- [ActivityRecord2] (invisible) diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 7d6cf4a63c43..677ec46007ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -26,7 +26,10 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLI import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_OWN_FOCUS; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -49,11 +52,13 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -67,12 +72,16 @@ import android.hardware.display.VirtualDisplay; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.InputConfig; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.util.MergedConfiguration; +import android.view.IWindow; import android.view.IWindowSessionCallback; +import android.view.InputChannel; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.Surface; @@ -690,6 +699,102 @@ public class WindowManagerServiceTests extends WindowTestsBase { assertEquals(validRect, resultingArgs.mSourceCrop); } + @Test + public void testGrantInputChannel_sanitizeSpyWindowForApplications() { + final Session session = mock(Session.class); + final int callingUid = Process.FIRST_APPLICATION_UID; + final int callingPid = 1234; + final SurfaceControl surfaceControl = mock(SurfaceControl.class); + final IWindow window = mock(IWindow.class); + final IBinder windowToken = mock(IBinder.class); + when(window.asBinder()).thenReturn(windowToken); + final IBinder focusGrantToken = mock(IBinder.class); + + final InputChannel inputChannel = new InputChannel(); + assertThrows(IllegalArgumentException.class, () -> + mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, + surfaceControl, window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, + PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY, TYPE_APPLICATION, + null /* windowToken */, focusGrantToken, "TestInputChannel", + inputChannel)); + } + + @Test + public void testGrantInputChannel_allowSpyWindowForInputMonitorPermission() { + final Session session = mock(Session.class); + final int callingUid = Process.SYSTEM_UID; + final int callingPid = 1234; + final SurfaceControl surfaceControl = mock(SurfaceControl.class); + final IWindow window = mock(IWindow.class); + final IBinder windowToken = mock(IBinder.class); + when(window.asBinder()).thenReturn(windowToken); + final IBinder focusGrantToken = mock(IBinder.class); + + final InputChannel inputChannel = new InputChannel(); + mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl, + window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, + INPUT_FEATURE_SPY, TYPE_APPLICATION, null /* windowToken */, focusGrantToken, + "TestInputChannel", inputChannel); + + verify(mTransaction).setInputWindowInfo( + eq(surfaceControl), + argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY)); + } + + @Test + public void testUpdateInputChannel_sanitizeSpyWindowForApplications() { + final Session session = mock(Session.class); + final int callingUid = Process.FIRST_APPLICATION_UID; + final int callingPid = 1234; + final SurfaceControl surfaceControl = mock(SurfaceControl.class); + final IWindow window = mock(IWindow.class); + final IBinder windowToken = mock(IBinder.class); + when(window.asBinder()).thenReturn(windowToken); + final IBinder focusGrantToken = mock(IBinder.class); + + final InputChannel inputChannel = new InputChannel(); + mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl, + window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, + 0 /* inputFeatures */, TYPE_APPLICATION, null /* windowToken */, focusGrantToken, + "TestInputChannel", inputChannel); + verify(mTransaction).setInputWindowInfo( + eq(surfaceControl), + argThat(h -> (h.inputConfig & InputConfig.SPY) == 0)); + + assertThrows(IllegalArgumentException.class, () -> + mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl, + FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY, + null /* region */)); + } + + @Test + public void testUpdateInputChannel_allowSpyWindowForInputMonitorPermission() { + final Session session = mock(Session.class); + final int callingUid = Process.SYSTEM_UID; + final int callingPid = 1234; + final SurfaceControl surfaceControl = mock(SurfaceControl.class); + final IWindow window = mock(IWindow.class); + final IBinder windowToken = mock(IBinder.class); + when(window.asBinder()).thenReturn(windowToken); + final IBinder focusGrantToken = mock(IBinder.class); + + final InputChannel inputChannel = new InputChannel(); + mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl, + window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, + 0 /* inputFeatures */, TYPE_APPLICATION, null /* windowToken */, focusGrantToken, + "TestInputChannel", inputChannel); + verify(mTransaction).setInputWindowInfo( + eq(surfaceControl), + argThat(h -> (h.inputConfig & InputConfig.SPY) == 0)); + + mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl, + FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY, + null /* region */); + verify(mTransaction).setInputWindowInfo( + eq(surfaceControl), + argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY)); + } + private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) { final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class); when(remoteToken.toWindowContainerToken()).thenReturn(wct); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java index 06fc41626e50..dbc824c384a2 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java @@ -68,6 +68,7 @@ import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SharedMemory; +import android.service.voice.DetectorFailure; import android.service.voice.HotwordDetectedResult; import android.service.voice.HotwordDetectionService; import android.service.voice.HotwordDetectionServiceFailure; @@ -623,11 +624,9 @@ abstract class DetectorSession { mRemoteDetectionService = remoteDetectionService; } - void reportErrorLocked(int errorCode, @NonNull String errorMessage) { + void reportErrorLocked(@NonNull DetectorFailure detectorFailure) { try { - // TODO: Use instanceof(this) to get different detector to set the right error source. - mCallback.onDetectionFailure( - new HotwordDetectionServiceFailure(errorCode, errorMessage)); + mCallback.onDetectionFailure(detectorFailure); } catch (RemoteException e) { Slog.w(TAG, "Failed to report onError status: " + e); if (getDetectorType() != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 025e1dc78c86..4fd5979b3d9f 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -51,11 +51,14 @@ import android.os.ServiceManager; import android.os.SharedMemory; import android.provider.DeviceConfig; import android.service.voice.HotwordDetectionService; +import android.service.voice.HotwordDetectionServiceFailure; import android.service.voice.HotwordDetector; import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback; import android.service.voice.ISandboxedDetectionService; import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback; +import android.service.voice.UnknownFailure; import android.service.voice.VisualQueryDetectionService; +import android.service.voice.VisualQueryDetectionServiceFailure; import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity; import android.speech.IRecognitionServiceManager; import android.util.Slog; @@ -109,6 +112,16 @@ final class HotwordDetectionConnection { private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour private static final int MAX_ISOLATED_PROCESS_NUMBER = 10; + /** + * Indicates the {@link HotwordDetectionService} is created. + */ + private static final int DETECTION_SERVICE_TYPE_HOTWORD = 1; + + /** + * Indicates the {@link VisualQueryDetectionService} is created. + */ + private static final int DETECTION_SERVICE_TYPE_VISUAL_QUERY = 2; + // TODO: This may need to be a Handler(looper) private final ScheduledExecutorService mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); @@ -186,11 +199,11 @@ final class HotwordDetectionConnection { mHotwordDetectionServiceConnectionFactory = new ServiceConnectionFactory(hotwordDetectionServiceIntent, - bindInstantServiceAllowed); + bindInstantServiceAllowed, DETECTION_SERVICE_TYPE_HOTWORD); mVisualQueryDetectionServiceConnectionFactory = new ServiceConnectionFactory(visualQueryDetectionServiceIntent, - bindInstantServiceAllowed); + bindInstantServiceAllowed, DETECTION_SERVICE_TYPE_VISUAL_QUERY); mLastRestartInstant = Instant.now(); @@ -604,17 +617,20 @@ final class HotwordDetectionConnection { private class ServiceConnectionFactory { private final Intent mIntent; private final int mBindingFlags; + private final int mDetectionServiceType; - ServiceConnectionFactory(@NonNull Intent intent, boolean bindInstantServiceAllowed) { + ServiceConnectionFactory(@NonNull Intent intent, boolean bindInstantServiceAllowed, + int detectionServiceType) { mIntent = intent; mBindingFlags = bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0; + mDetectionServiceType = detectionServiceType; } ServiceConnection createLocked() { ServiceConnection connection = new ServiceConnection(mContext, mIntent, mBindingFlags, mUser, ISandboxedDetectionService.Stub::asInterface, - mRestartCount % MAX_ISOLATED_PROCESS_NUMBER); + mRestartCount % MAX_ISOLATED_PROCESS_NUMBER, mDetectionServiceType); connection.connect(); updateAudioFlinger(connection, mAudioFlinger); @@ -635,15 +651,17 @@ final class HotwordDetectionConnection { private boolean mRespectServiceConnectionStatusChanged = true; private boolean mIsBound = false; private boolean mIsLoggedFirstConnect = false; + private final int mDetectionServiceType; ServiceConnection(@NonNull Context context, @NonNull Intent serviceIntent, int bindingFlags, int userId, @Nullable Function<IBinder, ISandboxedDetectionService> binderAsInterface, - int instanceNumber) { + int instanceNumber, int detectionServiceType) { super(context, serviceIntent, bindingFlags, userId, binderAsInterface); this.mIntent = serviceIntent; this.mBindingFlags = bindingFlags; this.mInstanceNumber = instanceNumber; + this.mDetectionServiceType = detectionServiceType; } @Override // from ServiceConnector.Impl @@ -660,14 +678,14 @@ final class HotwordDetectionConnection { mIsBound = connected; if (!connected) { - if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { + if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { HotwordMetricsLogger.writeDetectorEvent(mDetectorType, HOTWORD_DETECTOR_EVENTS__EVENT__ON_DISCONNECTED, mVoiceInteractionServiceUid); } } else if (!mIsLoggedFirstConnect) { mIsLoggedFirstConnect = true; - if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { + if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { HotwordMetricsLogger.writeDetectorEvent(mDetectorType, HOTWORD_DETECTOR_EVENTS__EVENT__ON_CONNECTED, mVoiceInteractionServiceUid); @@ -684,7 +702,7 @@ final class HotwordDetectionConnection { @Override public void binderDied() { super.binderDied(); - Slog.w(TAG, "binderDied"); + Slog.w(TAG, "binderDied mDetectionServiceType = " + mDetectionServiceType); synchronized (mLock) { if (!mRespectServiceConnectionStatusChanged) { Slog.v(TAG, "Ignored #binderDied event"); @@ -693,13 +711,10 @@ final class HotwordDetectionConnection { } //TODO(b265535257): report error to either service only. synchronized (HotwordDetectionConnection.this.mLock) { - runForEachDetectorSessionLocked((session) -> { - session.reportErrorLocked(DetectorSession.HOTWORD_DETECTION_SERVICE_DIED, - "Detection service is dead."); - }); + runForEachDetectorSessionLocked(this::reportBinderDiedLocked); } // Can improve to log exit reason if needed - if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { + if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__SERVICE_CRASH, @@ -711,7 +726,7 @@ final class HotwordDetectionConnection { protected boolean bindService( @NonNull android.content.ServiceConnection serviceConnection) { try { - if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { + if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { HotwordMetricsLogger.writeDetectorEvent(mDetectorType, HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE, mVoiceInteractionServiceUid); @@ -723,7 +738,12 @@ final class HotwordDetectionConnection { mExecutor, serviceConnection); if (!bindResult) { - if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { + Slog.w(TAG, + "bindService failure mDetectionServiceType = " + mDetectionServiceType); + synchronized (HotwordDetectionConnection.this.mLock) { + runForEachDetectorSessionLocked(this::reportBindServiceFailureLocked); + } + if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { HotwordMetricsLogger.writeDetectorEvent(mDetectorType, HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL, mVoiceInteractionServiceUid); @@ -731,7 +751,7 @@ final class HotwordDetectionConnection { } return bindResult; } catch (IllegalArgumentException e) { - if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { + if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { HotwordMetricsLogger.writeDetectorEvent(mDetectorType, HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL, mVoiceInteractionServiceUid); @@ -752,6 +772,42 @@ final class HotwordDetectionConnection { mRespectServiceConnectionStatusChanged = false; } } + + private void reportBinderDiedLocked(DetectorSession detectorSession) { + if (mDetectionServiceType == DETECTION_SERVICE_TYPE_HOTWORD && ( + detectorSession instanceof DspTrustedHotwordDetectorSession + || detectorSession instanceof SoftwareTrustedHotwordDetectorSession)) { + detectorSession.reportErrorLocked(new HotwordDetectionServiceFailure( + HotwordDetectionServiceFailure.ERROR_CODE_BINDING_DIED, + "Detection service is dead.")); + } else if (mDetectionServiceType == DETECTION_SERVICE_TYPE_VISUAL_QUERY + && detectorSession instanceof VisualQueryDetectorSession) { + detectorSession.reportErrorLocked(new VisualQueryDetectionServiceFailure( + VisualQueryDetectionServiceFailure.ERROR_CODE_BINDING_DIED, + "Detection service is dead.")); + } else { + detectorSession.reportErrorLocked(new UnknownFailure( + "Detection service is dead with unknown detection service type.")); + } + } + + private void reportBindServiceFailureLocked(DetectorSession detectorSession) { + if (mDetectionServiceType == DETECTION_SERVICE_TYPE_HOTWORD && ( + detectorSession instanceof DspTrustedHotwordDetectorSession + || detectorSession instanceof SoftwareTrustedHotwordDetectorSession)) { + detectorSession.reportErrorLocked(new HotwordDetectionServiceFailure( + HotwordDetectionServiceFailure.ERROR_CODE_BIND_FAILURE, + "Bind detection service failure.")); + } else if (mDetectionServiceType == DETECTION_SERVICE_TYPE_VISUAL_QUERY + && detectorSession instanceof VisualQueryDetectorSession) { + detectorSession.reportErrorLocked(new VisualQueryDetectionServiceFailure( + VisualQueryDetectionServiceFailure.ERROR_CODE_BIND_FAILURE, + "Bind detection service failure.")); + } else { + detectorSession.reportErrorLocked(new UnknownFailure( + "Bind detection service failure with unknown detection service type.")); + } + } } @SuppressWarnings("GuardedBy") diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 212dc41b3de3..7aa1334fd1c3 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -8654,6 +8654,16 @@ public class CarrierConfigManager { public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY = KEY_PREFIX + "epdg_address_priority_int_array"; + /** + * A priority list of PLMN to be used in EPDG_ADDRESS_PLMN. Possible values are {@link + * #EPDG_PLMN_RPLMN}, {@link #EPDG_PLMN_HPLMN}, {@link #EPDG_PLMN_EHPLMN_ALL}, {@link + * #EPDG_PLMN_EHPLMN_FIRST} + * + * @hide + */ + public static final String KEY_EPDG_PLMN_PRIORITY_INT_ARRAY = + KEY_PREFIX + "epdg_plmn_priority_int_array"; + /** Epdg static IP address or FQDN */ public static final String KEY_EPDG_STATIC_ADDRESS_STRING = KEY_PREFIX + "epdg_static_address_string"; @@ -8854,6 +8864,36 @@ public class CarrierConfigManager { public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4; /** @hide */ + @IntDef({ + EPDG_PLMN_RPLMN, + EPDG_PLMN_HPLMN, + EPDG_PLMN_EHPLMN_ALL, + EPDG_PLMN_EHPLMN_FIRST + }) + public @interface EpdgAddressPlmnType {} + + /** + * Use the Registered PLMN + * @hide + */ + public static final int EPDG_PLMN_RPLMN = 0; + /** + * Use the PLMN derived from IMSI + * @hide + */ + public static final int EPDG_PLMN_HPLMN = 1; + /** + * Use all EHPLMN from SIM EF files + * @hide + */ + public static final int EPDG_PLMN_EHPLMN_ALL = 2; + /** + * Use the first EHPLMN from SIM EF files + * @hide + */ + public static final int EPDG_PLMN_EHPLMN_FIRST = 3; + + /** @hide */ @IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID}) public @interface IkeIdType {} @@ -8988,6 +9028,12 @@ public class CarrierConfigManager { defaults.putIntArray( KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC}); + defaults.putIntArray( + KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + new int[]{ + EPDG_PLMN_RPLMN, + EPDG_PLMN_HPLMN, + EPDG_PLMN_EHPLMN_ALL}); defaults.putStringArray(KEY_MCC_MNCS_STRING_ARRAY, new String[0]); defaults.putInt(KEY_IKE_LOCAL_ID_TYPE_INT, ID_TYPE_RFC822_ADDR); defaults.putInt(KEY_IKE_REMOTE_ID_TYPE_INT, ID_TYPE_FQDN); diff --git a/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl index 24420833bdb6..a81444d51374 100644 --- a/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl +++ b/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl @@ -24,15 +24,22 @@ import android.telephony.satellite.PointingInfo; */ oneway interface ISatelliteTransmissionUpdateCallback { /** - * Called when satellite datagram transfer state changed. + * Called when satellite datagram send state changed. * - * @param state The new datagram transfer state. + * @param state The new send datagram transfer state. * @param sendPendingCount The number of datagrams that are currently being sent. - * @param receivePendingCount The number of datagrams that are currently being received. * @param errorCode If datagram transfer failed, the reason for failure. */ - void onDatagramTransferStateChanged(in int state, in int sendPendingCount, - in int receivePendingCount, in int errorCode); + void onSendDatagramStateChanged(in int state, in int sendPendingCount, in int errorCode); + + /** + * Called when satellite datagram receive state changed. + * + * @param state The new receive datagram transfer state. + * @param receivePendingCount The number of datagrams that are currently pending to be received. + * @param errorCode If datagram transfer failed, the reason for failure. + */ + void onReceiveDatagramStateChanged(in int state, in int receivePendingCount, in int errorCode); /** * Called when the satellite position changed. diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index d0abfbf80e76..e32566dc470f 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -647,6 +647,7 @@ public class SatelliteManager { }) @Retention(RetentionPolicy.SOURCE) public @interface SatelliteDatagramTransferState {} + // TODO: Split into two enums for sending and receiving states /** * Satellite modem is in idle state. @@ -750,19 +751,27 @@ public class SatelliteManager { }; ISatelliteTransmissionUpdateCallback internalCallback = new ISatelliteTransmissionUpdateCallback.Stub() { + @Override - public void onDatagramTransferStateChanged(int state, - int sendPendingCount, int receivePendingCount, int errorCode) { + public void onSatellitePositionChanged(PointingInfo pointingInfo) { executor.execute(() -> Binder.withCleanCallingIdentity( - () -> callback.onDatagramTransferStateChanged( - state, sendPendingCount, receivePendingCount, - errorCode))); + () -> callback.onSatellitePositionChanged(pointingInfo))); } @Override - public void onSatellitePositionChanged(PointingInfo pointingInfo) { + public void onSendDatagramStateChanged(int state, int sendPendingCount, + int errorCode) { executor.execute(() -> Binder.withCleanCallingIdentity( - () -> callback.onSatellitePositionChanged(pointingInfo))); + () -> callback.onSendDatagramStateChanged( + state, sendPendingCount, errorCode))); + } + + @Override + public void onReceiveDatagramStateChanged(int state, + int receivePendingCount, int errorCode) { + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> callback.onReceiveDatagramStateChanged( + state, receivePendingCount, errorCode))); } }; sSatelliteTransmissionUpdateCallbackMap.put(callback, internalCallback); diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java index 0efbd1fbdfef..d4fe57a0be2e 100644 --- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java +++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java @@ -33,14 +33,24 @@ public interface SatelliteTransmissionUpdateCallback { void onSatellitePositionChanged(@NonNull PointingInfo pointingInfo); /** - * Called when satellite datagram transfer state changed. + * Called when satellite datagram send state changed. * - * @param state The new datagram transfer state. + * @param state The new send datagram transfer state. * @param sendPendingCount The number of datagrams that are currently being sent. - * @param receivePendingCount The number of datagrams that are currently being received. * @param errorCode If datagram transfer failed, the reason for failure. */ - void onDatagramTransferStateChanged(@SatelliteManager.SatelliteDatagramTransferState int state, - int sendPendingCount, int receivePendingCount, + void onSendDatagramStateChanged( + @SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount, + @SatelliteManager.SatelliteError int errorCode); + + /** + * Called when satellite datagram receive state changed. + * + * @param state The new receive datagram transfer state. + * @param receivePendingCount The number of datagrams that are currently pending to be received. + * @param errorCode If datagram transfer failed, the reason for failure. + */ + void onReceiveDatagramStateChanged( + @SatelliteManager.SatelliteDatagramTransferState int state, int receivePendingCount, @SatelliteManager.SatelliteError int errorCode); } diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index 83893ba46885..a4c48fd5f342 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -13,6 +13,9 @@ android_test { "src/**/*.java", "src/**/*.kt", ], + kotlincflags: [ + "-Werror", + ], platform_apis: true, certificate: "platform", static_libs: [ diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt index 1d65cc35c3bc..0246426c1d33 100644 --- a/tests/Input/src/com/android/test/input/AnrTest.kt +++ b/tests/Input/src/com/android/test/input/AnrTest.kt @@ -73,7 +73,7 @@ class AnrTest { val contentResolver = instrumentation.targetContext.contentResolver hideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0) Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0) - PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage().getName() + PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.getName() } @After diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt index d83a4570fedc..3a24406e2b73 100644 --- a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt +++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt @@ -45,7 +45,8 @@ class UnresponsiveGestureMonitorActivity : Activity() { private lateinit var mInputMonitor: InputMonitor override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - mInputMonitor = InputManager.getInstance().monitorGestureInput(MONITOR_NAME, displayId) + val inputManager = getSystemService(InputManager::class.java) + mInputMonitor = inputManager.monitorGestureInput(MONITOR_NAME, displayId) mInputEventReceiver = UnresponsiveReceiver( mInputMonitor.getInputChannel(), Looper.myLooper()) } diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt index 423a6843891e..b4f6a1c12d8f 100644 --- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt +++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt @@ -36,7 +36,6 @@ class AndroidFrameworkIssueRegistry : IssueRegistry() { CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED, SaferParcelChecker.ISSUE_UNSAFE_API_USAGE, PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS, - RegisterReceiverFlagDetector.ISSUE_RECEIVER_EXPORTED_FLAG, PermissionMethodDetector.ISSUE_PERMISSION_METHOD_USAGE, PermissionMethodDetector.ISSUE_CAN_BE_PERMISSION_METHOD, ) diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt deleted file mode 100644 index c3e0428316c3..000000000000 --- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt +++ /dev/null @@ -1,927 +0,0 @@ -/* - * Copyright (C) 2021 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.google.android.lint - -import com.android.tools.lint.checks.DataFlowAnalyzer -import com.android.tools.lint.detector.api.Category -import com.android.tools.lint.detector.api.ConstantEvaluator -import com.android.tools.lint.detector.api.Detector -import com.android.tools.lint.detector.api.Implementation -import com.android.tools.lint.detector.api.Issue -import com.android.tools.lint.detector.api.JavaContext -import com.android.tools.lint.detector.api.Scope -import com.android.tools.lint.detector.api.Severity -import com.android.tools.lint.detector.api.SourceCodeScanner -import com.android.tools.lint.detector.api.UastLintUtils.Companion.findLastAssignment -import com.android.tools.lint.detector.api.getMethodName -import com.intellij.psi.PsiMethod -import com.intellij.psi.PsiVariable -import org.jetbrains.kotlin.psi.psiUtil.parameterIndex -import org.jetbrains.uast.UCallExpression -import org.jetbrains.uast.UElement -import org.jetbrains.uast.UExpression -import org.jetbrains.uast.UParenthesizedExpression -import org.jetbrains.uast.UQualifiedReferenceExpression -import org.jetbrains.uast.getContainingUMethod -import org.jetbrains.uast.isNullLiteral -import org.jetbrains.uast.skipParenthesizedExprDown -import org.jetbrains.uast.tryResolve - -/** - * Detector that identifies `registerReceiver()` calls which are missing the `RECEIVER_EXPORTED` or - * `RECEIVER_NOT_EXPORTED` flags on T+. - * - * TODO: Add API level conditions to better support non-platform code. - * 1. Check if registerReceiver() call is reachable on T+. - * 2. Check if targetSdkVersion is T+. - * - * eg: isWithinVersionCheckConditional(context, node, 31, false) - * eg: isPrecededByVersionCheckExit(context, node, 31) ? - */ -@Suppress("UnstableApiUsage") -class RegisterReceiverFlagDetector : Detector(), SourceCodeScanner { - - override fun getApplicableMethodNames(): List<String> = listOf( - "registerReceiver", - "registerReceiverAsUser", - "registerReceiverForAllUsers" - ) - - private fun checkIsProtectedReceiverAndReturnUnprotectedActions( - filterArg: UExpression, - node: UCallExpression, - evaluator: ConstantEvaluator - ): Pair<Boolean, List<String>> { // isProtected, unprotectedActions - val actions = mutableSetOf<String>() - val construction = findIntentFilterConstruction(filterArg, node) - - if (construction == null) return Pair(false, listOf<String>()) - val constructorActionArg = construction.getArgumentForParameter(0) - (constructorActionArg?.let(evaluator::evaluate) as? String)?.let(actions::add) - - val actionCollectorVisitor = - ActionCollectorVisitor(setOf(construction), node, evaluator) - - val parent = node.getContainingUMethod() - parent?.accept(actionCollectorVisitor) - actions.addAll(actionCollectorVisitor.actions) - - // If we failed to evaluate any actions, there will be a null action in the set. - val isProtected = - actions.all(PROTECTED_BROADCASTS::contains) && - !actionCollectorVisitor.intentFilterEscapesScope - val unprotectedActionsList = actions.filterNot(PROTECTED_BROADCASTS::contains) - return Pair(isProtected, unprotectedActionsList) - } - - override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { - if (!context.evaluator.isMemberInSubClassOf(method, "android.content.Context")) return - - // The parameter positions vary across the various registerReceiver*() methods, so rather - // than hardcode them we simply look them up based on the parameter name and type. - val receiverArg = - findArgument(node, method, "android.content.BroadcastReceiver", "receiver") - val filterArg = findArgument(node, method, "android.content.IntentFilter", "filter") - val flagsArg = findArgument(node, method, "int", "flags") - - if (receiverArg == null || receiverArg.isNullLiteral() || filterArg == null) { - return - } - - val evaluator = ConstantEvaluator().allowFieldInitializers() - - val (isProtected, unprotectedActionsList) = - checkIsProtectedReceiverAndReturnUnprotectedActions(filterArg, node, evaluator) - - val flags = evaluator.evaluate(flagsArg) as? Int - - if (!isProtected) { - val actionsList = unprotectedActionsList.joinToString(", ", "", "", -1, "") - val message = "$receiverArg is missing 'RECEIVED_EXPORTED` or 'RECEIVE_NOT_EXPORTED' " + - "flag for unprotected broadcast(s) registered for $actionsList." - if (flagsArg == null) { - context.report( - ISSUE_RECEIVER_EXPORTED_FLAG, node, context.getLocation(node), message) - } else if (flags != null && (flags and RECEIVER_EXPORTED_FLAG_PRESENT_MASK) == 0) { - context.report( - ISSUE_RECEIVER_EXPORTED_FLAG, node, context.getLocation(flagsArg), message) - } - } - - if (DEBUG) { - println(node.asRenderString()) - println("Unprotected Actions: $unprotectedActionsList") - println("Protected: $isProtected") - println("Flags: $flags") - } - } - - /** Finds the first argument of a method that matches the given parameter type and name. */ - private fun findArgument( - node: UCallExpression, - method: PsiMethod, - type: String, - name: String - ): UExpression? { - val psiParameter = method.parameterList.parameters.firstOrNull { - it.type.canonicalText == type && it.name == name - } ?: return null - val argument = node.getArgumentForParameter(psiParameter.parameterIndex()) - return argument?.skipParenthesizedExprDown() - } - - /** - * For the supplied expression (eg. intent filter argument), attempts to find its construction. - * This will be an `IntentFilter()` constructor, an `IntentFilter.create()` call, or `null`. - */ - private fun findIntentFilterConstruction( - expression: UExpression, - node: UCallExpression - ): UCallExpression? { - val resolved = expression.tryResolve() - - if (resolved is PsiVariable) { - val assignment = findLastAssignment(resolved, node) ?: return null - return findIntentFilterConstruction(assignment, node) - } - - if (expression is UParenthesizedExpression) { - return findIntentFilterConstruction(expression.expression, node) - } - - if (expression is UQualifiedReferenceExpression) { - val call = expression.selector as? UCallExpression ?: return null - return if (isReturningContext(call)) { - // eg. filter.apply { addAction("abc") } --> use filter variable. - findIntentFilterConstruction(expression.receiver, node) - } else { - // eg. IntentFilter.create("abc") --> use create("abc") UCallExpression. - findIntentFilterConstruction(call, node) - } - } - - val method = resolved as? PsiMethod ?: return null - return if (isIntentFilterFactoryMethod(method)) { - expression as? UCallExpression - } else { - null - } - } - - private fun isIntentFilterFactoryMethod(method: PsiMethod) = - (method.containingClass?.qualifiedName == "android.content.IntentFilter" && - (method.returnType?.canonicalText == "android.content.IntentFilter" || - method.isConstructor)) - - /** - * Returns true if the given call represents a Kotlin scope function where the return value is - * the context object; see https://kotlinlang.org/docs/scope-functions.html#function-selection. - */ - private fun isReturningContext(node: UCallExpression): Boolean { - val name = getMethodName(node) - if (name == "apply" || name == "also") { - return isScopingFunction(node) - } - return false - } - - /** - * Returns true if the given node appears to be one of the scope functions. Only checks parent - * class; caller should intend that it's actually one of let, with, apply, etc. - */ - private fun isScopingFunction(node: UCallExpression): Boolean { - val called = node.resolve() ?: return true - // See libraries/stdlib/jvm/build/stdlib-declarations.json - return called.containingClass?.qualifiedName == "kotlin.StandardKt__StandardKt" - } - - inner class ActionCollectorVisitor( - start: Collection<UElement>, - val functionCall: UCallExpression, - val evaluator: ConstantEvaluator, - ) : DataFlowAnalyzer(start) { - private var finished = false - var intentFilterEscapesScope = false; private set - val actions = mutableSetOf<String>() - - override fun argument(call: UCallExpression, reference: UElement) { - // TODO: Remove this temporary fix for DataFlowAnalyzer bug (ag/15787550): - if (reference !in call.valueArguments) return - val methodNames = super@RegisterReceiverFlagDetector.getApplicableMethodNames() - when { - finished -> return - // We've reached the registerReceiver*() call in question. - call == functionCall -> finished = true - // The filter 'intentFilterEscapesScope' to a method which could modify it. - methodNames != null && getMethodName(call)!! !in methodNames -> - intentFilterEscapesScope = true - } - } - - // Fixed in b/199163915: DataFlowAnalyzer doesn't call this for Kotlin properties. - override fun field(field: UElement) { - if (!finished) intentFilterEscapesScope = true - } - - override fun receiver(call: UCallExpression) { - if (!finished && getMethodName(call) == "addAction") { - val actionArg = call.getArgumentForParameter(0) - if (actionArg != null) { - val action = evaluator.evaluate(actionArg) as? String - if (action != null) actions.add(action) - } - } - } - } - - companion object { - const val DEBUG = false - - private const val RECEIVER_EXPORTED = 0x2 - private const val RECEIVER_NOT_EXPORTED = 0x4 - private const val RECEIVER_EXPORTED_FLAG_PRESENT_MASK = - RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED - - @JvmField - val ISSUE_RECEIVER_EXPORTED_FLAG: Issue = Issue.create( - id = "UnspecifiedRegisterReceiverFlag", - briefDescription = "Missing `registerReceiver()` exported flag", - explanation = """ - Apps targeting Android T (SDK 33) and higher must specify either `RECEIVER_EXPORTED` \ - or `RECEIVER_NOT_EXPORTED` when registering a broadcast receiver, unless the \ - receiver is only registered for protected system broadcast actions. - """, - category = Category.SECURITY, - priority = 5, - severity = Severity.WARNING, - implementation = Implementation( - RegisterReceiverFlagDetector::class.java, - Scope.JAVA_FILE_SCOPE - ) - ) - - val PROTECTED_BROADCASTS = listOf( - "android.intent.action.SCREEN_OFF", - "android.intent.action.SCREEN_ON", - "android.intent.action.USER_PRESENT", - "android.intent.action.TIME_SET", - "android.intent.action.TIME_TICK", - "android.intent.action.TIMEZONE_CHANGED", - "android.intent.action.DATE_CHANGED", - "android.intent.action.PRE_BOOT_COMPLETED", - "android.intent.action.BOOT_COMPLETED", - "android.intent.action.PACKAGE_INSTALL", - "android.intent.action.PACKAGE_ADDED", - "android.intent.action.PACKAGE_REPLACED", - "android.intent.action.MY_PACKAGE_REPLACED", - "android.intent.action.PACKAGE_REMOVED", - "android.intent.action.PACKAGE_REMOVED_INTERNAL", - "android.intent.action.PACKAGE_FULLY_REMOVED", - "android.intent.action.PACKAGE_CHANGED", - "android.intent.action.PACKAGE_FULLY_LOADED", - "android.intent.action.PACKAGE_ENABLE_ROLLBACK", - "android.intent.action.CANCEL_ENABLE_ROLLBACK", - "android.intent.action.ROLLBACK_COMMITTED", - "android.intent.action.PACKAGE_RESTARTED", - "android.intent.action.PACKAGE_DATA_CLEARED", - "android.intent.action.PACKAGE_FIRST_LAUNCH", - "android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION", - "android.intent.action.PACKAGE_NEEDS_VERIFICATION", - "android.intent.action.PACKAGE_VERIFIED", - "android.intent.action.PACKAGES_SUSPENDED", - "android.intent.action.PACKAGES_UNSUSPENDED", - "android.intent.action.PACKAGES_SUSPENSION_CHANGED", - "android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY", - "android.intent.action.DISTRACTING_PACKAGES_CHANGED", - "android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED", - "android.intent.action.UID_REMOVED", - "android.intent.action.QUERY_PACKAGE_RESTART", - "android.intent.action.CONFIGURATION_CHANGED", - "android.intent.action.SPLIT_CONFIGURATION_CHANGED", - "android.intent.action.LOCALE_CHANGED", - "android.intent.action.APPLICATION_LOCALE_CHANGED", - "android.intent.action.BATTERY_CHANGED", - "android.intent.action.BATTERY_LEVEL_CHANGED", - "android.intent.action.BATTERY_LOW", - "android.intent.action.BATTERY_OKAY", - "android.intent.action.ACTION_POWER_CONNECTED", - "android.intent.action.ACTION_POWER_DISCONNECTED", - "android.intent.action.ACTION_SHUTDOWN", - "android.intent.action.CHARGING", - "android.intent.action.DISCHARGING", - "android.intent.action.DEVICE_STORAGE_LOW", - "android.intent.action.DEVICE_STORAGE_OK", - "android.intent.action.DEVICE_STORAGE_FULL", - "android.intent.action.DEVICE_STORAGE_NOT_FULL", - "android.intent.action.NEW_OUTGOING_CALL", - "android.intent.action.REBOOT", - "android.intent.action.DOCK_EVENT", - "android.intent.action.THERMAL_EVENT", - "android.intent.action.MASTER_CLEAR_NOTIFICATION", - "android.intent.action.USER_ADDED", - "android.intent.action.USER_REMOVED", - "android.intent.action.USER_STARTING", - "android.intent.action.USER_STARTED", - "android.intent.action.USER_STOPPING", - "android.intent.action.USER_STOPPED", - "android.intent.action.USER_BACKGROUND", - "android.intent.action.USER_FOREGROUND", - "android.intent.action.USER_SWITCHED", - "android.intent.action.USER_INITIALIZE", - "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION", - "android.intent.action.DOMAINS_NEED_VERIFICATION", - "android.intent.action.OVERLAY_ADDED", - "android.intent.action.OVERLAY_CHANGED", - "android.intent.action.OVERLAY_REMOVED", - "android.intent.action.OVERLAY_PRIORITY_CHANGED", - "android.intent.action.MY_PACKAGE_SUSPENDED", - "android.intent.action.MY_PACKAGE_UNSUSPENDED", - "android.os.action.POWER_SAVE_MODE_CHANGED", - "android.os.action.DEVICE_IDLE_MODE_CHANGED", - "android.os.action.POWER_SAVE_WHITELIST_CHANGED", - "android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED", - "android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL", - "android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED", - "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED", - "android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED", - "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL", - "android.app.action.ENTER_CAR_MODE", - "android.app.action.EXIT_CAR_MODE", - "android.app.action.ENTER_CAR_MODE_PRIORITIZED", - "android.app.action.EXIT_CAR_MODE_PRIORITIZED", - "android.app.action.ENTER_DESK_MODE", - "android.app.action.EXIT_DESK_MODE", - "android.app.action.NEXT_ALARM_CLOCK_CHANGED", - "android.app.action.USER_ADDED", - "android.app.action.USER_REMOVED", - "android.app.action.USER_STARTED", - "android.app.action.USER_STOPPED", - "android.app.action.USER_SWITCHED", - "android.app.action.BUGREPORT_SHARING_DECLINED", - "android.app.action.BUGREPORT_FAILED", - "android.app.action.BUGREPORT_SHARE", - "android.app.action.SHOW_DEVICE_MONITORING_DIALOG", - "android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED", - "android.intent.action.INCIDENT_REPORT_READY", - "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS", - "android.appwidget.action.APPWIDGET_DELETED", - "android.appwidget.action.APPWIDGET_DISABLED", - "android.appwidget.action.APPWIDGET_ENABLED", - "android.appwidget.action.APPWIDGET_HOST_RESTORED", - "android.appwidget.action.APPWIDGET_RESTORED", - "android.appwidget.action.APPWIDGET_ENABLE_AND_UPDATE", - "android.os.action.SETTING_RESTORED", - "android.app.backup.intent.CLEAR", - "android.app.backup.intent.INIT", - "android.bluetooth.intent.DISCOVERABLE_TIMEOUT", - "android.bluetooth.adapter.action.STATE_CHANGED", - "android.bluetooth.adapter.action.SCAN_MODE_CHANGED", - "android.bluetooth.adapter.action.DISCOVERY_STARTED", - "android.bluetooth.adapter.action.DISCOVERY_FINISHED", - "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED", - "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED", - "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.device.action.UUID", - "android.bluetooth.device.action.MAS_INSTANCE", - "android.bluetooth.device.action.ALIAS_CHANGED", - "android.bluetooth.device.action.FOUND", - "android.bluetooth.device.action.CLASS_CHANGED", - "android.bluetooth.device.action.ACL_CONNECTED", - "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED", - "android.bluetooth.device.action.ACL_DISCONNECTED", - "android.bluetooth.device.action.NAME_CHANGED", - "android.bluetooth.device.action.BOND_STATE_CHANGED", - "android.bluetooth.device.action.NAME_FAILED", - "android.bluetooth.device.action.PAIRING_REQUEST", - "android.bluetooth.device.action.PAIRING_CANCEL", - "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY", - "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL", - "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST", - "android.bluetooth.device.action.SDP_RECORD", - "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED", - "android.bluetooth.devicepicker.action.LAUNCH", - "android.bluetooth.devicepicker.action.DEVICE_SELECTED", - "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED", - "android.bluetooth.action.CSIS_DEVICE_AVAILABLE", - "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE", - "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED", - "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY", - "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY", - "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED", - "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED", - "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED", - "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED", - "android.bluetooth.action.LE_AUDIO_CONF_CHANGED", - "android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED", - "android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED", - "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED", - "android.btopp.intent.action.INCOMING_FILE_NOTIFICATION", - "android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT", - "android.btopp.intent.action.LIST", - "android.btopp.intent.action.OPEN_OUTBOUND", - "android.btopp.intent.action.HIDE_COMPLETE", - "android.btopp.intent.action.CONFIRM", - "android.btopp.intent.action.HIDE", - "android.btopp.intent.action.RETRY", - "android.btopp.intent.action.OPEN", - "android.btopp.intent.action.OPEN_INBOUND", - "android.btopp.intent.action.TRANSFER_COMPLETE", - "android.btopp.intent.action.ACCEPT", - "android.btopp.intent.action.DECLINE", - "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN", - "com.android.bluetooth.pbap.authchall", - "com.android.bluetooth.pbap.userconfirmtimeout", - "com.android.bluetooth.pbap.authresponse", - "com.android.bluetooth.pbap.authcancelled", - "com.android.bluetooth.sap.USER_CONFIRM_TIMEOUT", - "com.android.bluetooth.sap.action.DISCONNECT_ACTION", - "android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED", - "android.hardware.usb.action.USB_STATE", - "android.hardware.usb.action.USB_PORT_CHANGED", - "android.hardware.usb.action.USB_ACCESSORY_ATTACHED", - "android.hardware.usb.action.USB_ACCESSORY_DETACHED", - "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE", - "android.hardware.usb.action.USB_DEVICE_ATTACHED", - "android.hardware.usb.action.USB_DEVICE_DETACHED", - "android.intent.action.HEADSET_PLUG", - "android.media.action.HDMI_AUDIO_PLUG", - "android.media.action.MICROPHONE_MUTE_CHANGED", - "android.media.action.SPEAKERPHONE_STATE_CHANGED", - "android.media.AUDIO_BECOMING_NOISY", - "android.media.RINGER_MODE_CHANGED", - "android.media.VIBRATE_SETTING_CHANGED", - "android.media.VOLUME_CHANGED_ACTION", - "android.media.MASTER_VOLUME_CHANGED_ACTION", - "android.media.MASTER_MUTE_CHANGED_ACTION", - "android.media.MASTER_MONO_CHANGED_ACTION", - "android.media.MASTER_BALANCE_CHANGED_ACTION", - "android.media.SCO_AUDIO_STATE_CHANGED", - "android.media.ACTION_SCO_AUDIO_STATE_UPDATED", - "android.intent.action.MEDIA_REMOVED", - "android.intent.action.MEDIA_UNMOUNTED", - "android.intent.action.MEDIA_CHECKING", - "android.intent.action.MEDIA_NOFS", - "android.intent.action.MEDIA_MOUNTED", - "android.intent.action.MEDIA_SHARED", - "android.intent.action.MEDIA_UNSHARED", - "android.intent.action.MEDIA_BAD_REMOVAL", - "android.intent.action.MEDIA_UNMOUNTABLE", - "android.intent.action.MEDIA_EJECT", - "android.net.conn.CAPTIVE_PORTAL", - "android.net.conn.CONNECTIVITY_CHANGE", - "android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE", - "android.net.conn.DATA_ACTIVITY_CHANGE", - "android.net.conn.RESTRICT_BACKGROUND_CHANGED", - "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED", - "android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED", - "android.net.nsd.STATE_CHANGED", - "android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED", - "android.nfc.action.ADAPTER_STATE_CHANGED", - "android.nfc.action.PREFERRED_PAYMENT_CHANGED", - "android.nfc.action.TRANSACTION_DETECTED", - "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC", - "com.android.nfc.action.LLCP_UP", - "com.android.nfc.action.LLCP_DOWN", - "com.android.nfc.cardemulation.action.CLOSE_TAP_DIALOG", - "com.android.nfc.handover.action.ALLOW_CONNECT", - "com.android.nfc.handover.action.DENY_CONNECT", - "com.android.nfc.handover.action.TIMEOUT_CONNECT", - "com.android.nfc_extras.action.RF_FIELD_ON_DETECTED", - "com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED", - "com.android.nfc_extras.action.AID_SELECTED", - "android.btopp.intent.action.WHITELIST_DEVICE", - "android.btopp.intent.action.STOP_HANDOVER_TRANSFER", - "android.nfc.handover.intent.action.HANDOVER_SEND", - "android.nfc.handover.intent.action.HANDOVER_SEND_MULTIPLE", - "com.android.nfc.handover.action.CANCEL_HANDOVER_TRANSFER", - "android.net.action.CLEAR_DNS_CACHE", - "android.intent.action.PROXY_CHANGE", - "android.os.UpdateLock.UPDATE_LOCK_CHANGED", - "android.intent.action.DREAMING_STARTED", - "android.intent.action.DREAMING_STOPPED", - "android.intent.action.ANY_DATA_STATE", - "com.android.server.stats.action.TRIGGER_COLLECTION", - "com.android.server.WifiManager.action.START_SCAN", - "com.android.server.WifiManager.action.START_PNO", - "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP", - "com.android.server.WifiManager.action.DEVICE_IDLE", - "com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED", - "com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED", - "com.android.internal.action.EUICC_FACTORY_RESET", - "com.android.server.usb.ACTION_OPEN_IN_APPS", - "com.android.server.am.DELETE_DUMPHEAP", - "com.android.server.net.action.SNOOZE_WARNING", - "com.android.server.net.action.SNOOZE_RAPID", - "com.android.server.wifi.ACTION_SHOW_SET_RANDOMIZATION_DETAILS", - "com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_APP", - "com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_APP", - "com.android.server.wifi.action.NetworkSuggestion.USER_DISMISSED", - "com.android.server.wifi.action.CarrierNetwork.USER_ALLOWED_CARRIER", - "com.android.server.wifi.action.CarrierNetwork.USER_DISALLOWED_CARRIER", - "com.android.server.wifi.action.CarrierNetwork.USER_DISMISSED", - "com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION", - "com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK", - "com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK", - "com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE", - "com.android.server.wifi.wakeup.DISMISS_NOTIFICATION", - "com.android.server.wifi.wakeup.OPEN_WIFI_PREFERENCES", - "com.android.server.wifi.wakeup.OPEN_WIFI_SETTINGS", - "com.android.server.wifi.wakeup.TURN_OFF_WIFI_WAKE", - "android.net.wifi.WIFI_STATE_CHANGED", - "android.net.wifi.WIFI_AP_STATE_CHANGED", - "android.net.wifi.WIFI_CREDENTIAL_CHANGED", - "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED", - "android.net.wifi.aware.action.WIFI_AWARE_RESOURCE_CHANGED", - "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED", - "android.net.wifi.SCAN_RESULTS", - "android.net.wifi.RSSI_CHANGED", - "android.net.wifi.STATE_CHANGE", - "android.net.wifi.LINK_CONFIGURATION_CHANGED", - "android.net.wifi.CONFIGURED_NETWORKS_CHANGE", - "android.net.wifi.action.NETWORK_SETTINGS_RESET", - "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT", - "android.net.wifi.action.PASSPOINT_ICON", - "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST", - "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION", - "android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW", - "android.net.wifi.action.REFRESH_USER_PROVISIONING", - "android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION", - "android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED", - "android.net.wifi.supplicant.CONNECTION_CHANGE", - "android.net.wifi.supplicant.STATE_CHANGE", - "android.net.wifi.p2p.STATE_CHANGED", - "android.net.wifi.p2p.DISCOVERY_STATE_CHANGE", - "android.net.wifi.p2p.THIS_DEVICE_CHANGED", - "android.net.wifi.p2p.PEERS_CHANGED", - "android.net.wifi.p2p.CONNECTION_STATE_CHANGE", - "android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED", - "android.net.conn.TETHER_STATE_CHANGED", - "android.net.conn.INET_CONDITION_ACTION", - "android.net.conn.NETWORK_CONDITIONS_MEASURED", - "android.net.scoring.SCORE_NETWORKS", - "android.net.scoring.SCORER_CHANGED", - "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE", - "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE", - "android.intent.action.AIRPLANE_MODE", - "android.intent.action.ADVANCED_SETTINGS", - "android.intent.action.APPLICATION_RESTRICTIONS_CHANGED", - "com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES", - "com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT", - "com.android.server.adb.WIRELESS_DEBUG_STATUS", - "android.intent.action.ACTION_IDLE_MAINTENANCE_START", - "android.intent.action.ACTION_IDLE_MAINTENANCE_END", - "com.android.server.ACTION_TRIGGER_IDLE", - "android.intent.action.HDMI_PLUGGED", - "android.intent.action.PHONE_STATE", - "android.intent.action.SUB_DEFAULT_CHANGED", - "android.location.PROVIDERS_CHANGED", - "android.location.MODE_CHANGED", - "android.location.action.GNSS_CAPABILITIES_CHANGED", - "android.net.proxy.PAC_REFRESH", - "android.telecom.action.DEFAULT_DIALER_CHANGED", - "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED", - "android.provider.action.SMS_MMS_DB_CREATED", - "android.provider.action.SMS_MMS_DB_LOST", - "android.intent.action.CONTENT_CHANGED", - "android.provider.Telephony.MMS_DOWNLOADED", - "android.content.action.PERMISSION_RESPONSE_RECEIVED", - "android.content.action.REQUEST_PERMISSION", - "android.nfc.handover.intent.action.HANDOVER_STARTED", - "android.nfc.handover.intent.action.TRANSFER_DONE", - "android.nfc.handover.intent.action.TRANSFER_PROGRESS", - "android.nfc.handover.intent.action.TRANSFER_DONE", - "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED", - "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED", - "android.intent.action.ACTION_SET_RADIO_CAPABILITY_DONE", - "android.intent.action.ACTION_SET_RADIO_CAPABILITY_FAILED", - "android.internal.policy.action.BURN_IN_PROTECTION", - "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED", - "android.app.action.RESET_PROTECTION_POLICY_CHANGED", - "android.app.action.DEVICE_OWNER_CHANGED", - "android.app.action.MANAGED_USER_CREATED", - "android.intent.action.ANR", - "android.intent.action.CALL", - "android.intent.action.CALL_PRIVILEGED", - "android.intent.action.DROPBOX_ENTRY_ADDED", - "android.intent.action.INPUT_METHOD_CHANGED", - "android.intent.action.internal_sim_state_changed", - "android.intent.action.LOCKED_BOOT_COMPLETED", - "android.intent.action.PRECISE_CALL_STATE", - "android.intent.action.SUBSCRIPTION_PHONE_STATE", - "android.intent.action.USER_INFO_CHANGED", - "android.intent.action.USER_UNLOCKED", - "android.intent.action.WALLPAPER_CHANGED", - "android.app.action.DEVICE_POLICY_MANAGER_STATE_CHANGED", - "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS", - "android.app.action.DEVICE_ADMIN_DISABLED", - "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED", - "android.app.action.DEVICE_ADMIN_ENABLED", - "android.app.action.LOCK_TASK_ENTERING", - "android.app.action.LOCK_TASK_EXITING", - "android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE", - "android.app.action.ACTION_PASSWORD_CHANGED", - "android.app.action.ACTION_PASSWORD_EXPIRING", - "android.app.action.ACTION_PASSWORD_FAILED", - "android.app.action.ACTION_PASSWORD_SUCCEEDED", - "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION", - "com.android.server.ACTION_PROFILE_OFF_DEADLINE", - "com.android.server.ACTION_TURN_PROFILE_ON_NOTIFICATION", - "android.intent.action.MANAGED_PROFILE_ADDED", - "android.intent.action.MANAGED_PROFILE_UNLOCKED", - "android.intent.action.MANAGED_PROFILE_REMOVED", - "android.app.action.MANAGED_PROFILE_PROVISIONED", - "android.bluetooth.adapter.action.BLE_STATE_CHANGED", - "com.android.bluetooth.map.USER_CONFIRM_TIMEOUT", - "com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT", - "com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY", - "android.content.jobscheduler.JOB_DELAY_EXPIRED", - "android.content.syncmanager.SYNC_ALARM", - "android.media.INTERNAL_RINGER_MODE_CHANGED_ACTION", - "android.media.STREAM_DEVICES_CHANGED_ACTION", - "android.media.STREAM_MUTE_CHANGED_ACTION", - "android.net.sip.SIP_SERVICE_UP", - "android.nfc.action.ADAPTER_STATE_CHANGED", - "android.os.action.CHARGING", - "android.os.action.DISCHARGING", - "android.search.action.SEARCHABLES_CHANGED", - "android.security.STORAGE_CHANGED", - "android.security.action.TRUST_STORE_CHANGED", - "android.security.action.KEYCHAIN_CHANGED", - "android.security.action.KEY_ACCESS_CHANGED", - "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED", - "android.telecom.action.PHONE_ACCOUNT_REGISTERED", - "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED", - "android.telecom.action.POST_CALL", - "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION", - "android.telephony.action.CARRIER_CONFIG_CHANGED", - "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED", - "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED", - "android.telephony.action.SECRET_CODE", - "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION", - "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED", - "com.android.bluetooth.btservice.action.ALARM_WAKEUP", - "com.android.server.action.NETWORK_STATS_POLL", - "com.android.server.action.NETWORK_STATS_UPDATED", - "com.android.server.timedetector.NetworkTimeUpdateService.action.POLL", - "com.android.server.telecom.intent.action.CALLS_ADD_ENTRY", - "com.android.settings.location.MODE_CHANGING", - "com.android.settings.bluetooth.ACTION_DISMISS_PAIRING", - "com.android.settings.network.DELETE_SUBSCRIPTION", - "com.android.settings.network.SWITCH_TO_SUBSCRIPTION", - "com.android.settings.wifi.action.NETWORK_REQUEST", - "NotificationManagerService.TIMEOUT", - "NotificationHistoryDatabase.CLEANUP", - "ScheduleConditionProvider.EVALUATE", - "EventConditionProvider.EVALUATE", - "SnoozeHelper.EVALUATE", - "wifi_scan_available", - "action.cne.started", - "android.content.jobscheduler.JOB_DEADLINE_EXPIRED", - "android.intent.action.ACTION_UNSOL_RESPONSE_OEM_HOOK_RAW", - "android.net.conn.CONNECTIVITY_CHANGE_SUPL", - "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED", - "android.os.storage.action.VOLUME_STATE_CHANGED", - "android.os.storage.action.DISK_SCANNED", - "com.android.server.action.UPDATE_TWILIGHT_STATE", - "com.android.server.action.RESET_TWILIGHT_AUTO", - "com.android.server.device_idle.STEP_IDLE_STATE", - "com.android.server.device_idle.STEP_LIGHT_IDLE_STATE", - "com.android.server.Wifi.action.TOGGLE_PNO", - "intent.action.ACTION_RF_BAND_INFO", - "android.intent.action.MEDIA_RESOURCE_GRANTED", - "android.app.action.NETWORK_LOGS_AVAILABLE", - "android.app.action.SECURITY_LOGS_AVAILABLE", - "android.app.action.COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED", - "android.app.action.INTERRUPTION_FILTER_CHANGED", - "android.app.action.INTERRUPTION_FILTER_CHANGED_INTERNAL", - "android.app.action.NOTIFICATION_POLICY_CHANGED", - "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED", - "android.app.action.AUTOMATIC_ZEN_RULE_STATUS_CHANGED", - "android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED", - "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED", - "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED", - "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED", - "android.app.action.APP_BLOCK_STATE_CHANGED", - "android.permission.GET_APP_GRANTED_URI_PERMISSIONS", - "android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS", - "android.intent.action.DYNAMIC_SENSOR_CHANGED", - "android.accounts.LOGIN_ACCOUNTS_CHANGED", - "android.accounts.action.ACCOUNT_REMOVED", - "android.accounts.action.VISIBLE_ACCOUNTS_CHANGED", - "com.android.sync.SYNC_CONN_STATUS_CHANGED", - "android.net.sip.action.SIP_INCOMING_CALL", - "com.android.phone.SIP_ADD_PHONE", - "android.net.sip.action.SIP_REMOVE_PROFILE", - "android.net.sip.action.SIP_SERVICE_UP", - "android.net.sip.action.SIP_CALL_OPTION_CHANGED", - "android.net.sip.action.START_SIP", - "android.bluetooth.adapter.action.BLE_ACL_CONNECTED", - "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED", - "android.bluetooth.input.profile.action.HANDSHAKE", - "android.bluetooth.input.profile.action.REPORT", - "android.intent.action.TWILIGHT_CHANGED", - "com.android.server.fingerprint.ACTION_LOCKOUT_RESET", - "android.net.wifi.PASSPOINT_ICON_RECEIVED", - "com.android.server.notification.CountdownConditionProvider", - "android.server.notification.action.ENABLE_NAS", - "android.server.notification.action.DISABLE_NAS", - "android.server.notification.action.LEARNMORE_NAS", - "com.android.internal.location.ALARM_WAKEUP", - "com.android.internal.location.ALARM_TIMEOUT", - "android.intent.action.GLOBAL_BUTTON", - "android.intent.action.MANAGED_PROFILE_AVAILABLE", - "android.intent.action.MANAGED_PROFILE_UNAVAILABLE", - "com.android.server.pm.DISABLE_QUIET_MODE_AFTER_UNLOCK", - "android.intent.action.PROFILE_ACCESSIBLE", - "android.intent.action.PROFILE_INACCESSIBLE", - "com.android.server.retaildemo.ACTION_RESET_DEMO", - "android.intent.action.DEVICE_LOCKED_CHANGED", - "com.android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED", - "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED", - "com.android.server.wm.ACTION_REVOKE_SYSTEM_ALERT_WINDOW_PERMISSION", - "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED", - "android.content.pm.action.SESSION_COMMITTED", - "android.os.action.USER_RESTRICTIONS_CHANGED", - "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT", - "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED", - "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED", - "android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED", - "com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER", - "com.android.intent.action.timezone.RULES_UPDATE_OPERATION", - "com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK", - "android.intent.action.GET_RESTRICTION_ENTRIES", - "android.telephony.euicc.action.OTA_STATUS_CHANGED", - "android.app.action.PROFILE_OWNER_CHANGED", - "android.app.action.TRANSFER_OWNERSHIP_COMPLETE", - "android.app.action.AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE", - "android.app.action.STATSD_STARTED", - "com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET", - "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET", - "android.intent.action.DOCK_IDLE", - "android.intent.action.DOCK_ACTIVE", - "android.content.pm.action.SESSION_UPDATED", - "android.settings.action.GRAYSCALE_CHANGED", - "com.android.server.jobscheduler.GARAGE_MODE_ON", - "com.android.server.jobscheduler.GARAGE_MODE_OFF", - "com.android.server.jobscheduler.FORCE_IDLE", - "com.android.server.jobscheduler.UNFORCE_IDLE", - "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL", - "android.intent.action.DEVICE_CUSTOMIZATION_READY", - "android.app.action.RESET_PROTECTION_POLICY_CHANGED", - "com.android.internal.intent.action.BUGREPORT_REQUESTED", - "android.scheduling.action.REBOOT_READY", - "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED", - "android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED", - "android.app.action.SHOW_NEW_USER_DISCLAIMER", - "android.telecom.action.CURRENT_TTY_MODE_CHANGED", - "android.intent.action.SERVICE_STATE", - "android.intent.action.RADIO_TECHNOLOGY", - "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED", - "android.intent.action.EMERGENCY_CALL_STATE_CHANGED", - "android.intent.action.SIG_STR", - "android.intent.action.ANY_DATA_STATE", - "android.intent.action.DATA_STALL_DETECTED", - "android.intent.action.SIM_STATE_CHANGED", - "android.intent.action.USER_ACTIVITY_NOTIFICATION", - "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS", - "android.intent.action.ACTION_MDN_STATE_CHANGED", - "android.telephony.action.SERVICE_PROVIDERS_UPDATED", - "android.provider.Telephony.SIM_FULL", - "com.android.internal.telephony.carrier_key_download_alarm", - "com.android.internal.telephony.data-restart-trysetup", - "com.android.internal.telephony.data-stall", - "com.android.internal.telephony.provisioning_apn_alarm", - "android.intent.action.DATA_SMS_RECEIVED", - "android.provider.Telephony.SMS_RECEIVED", - "android.provider.Telephony.SMS_DELIVER", - "android.provider.Telephony.SMS_REJECTED", - "android.provider.Telephony.WAP_PUSH_DELIVER", - "android.provider.Telephony.WAP_PUSH_RECEIVED", - "android.provider.Telephony.SMS_CB_RECEIVED", - "android.provider.action.SMS_EMERGENCY_CB_RECEIVED", - "android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED", - "android.provider.Telephony.SECRET_CODE", - "com.android.internal.stk.command", - "com.android.internal.stk.session_end", - "com.android.internal.stk.icc_status_change", - "com.android.internal.stk.alpha_notify", - "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED", - "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED", - "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE", - "com.android.internal.telephony.CARRIER_SIGNAL_RESET", - "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE", - "com.android.internal.telephony.PROVISION", - "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED", - "com.android.internal.provider.action.VOICEMAIL_SMS_RECEIVED", - "com.android.intent.isim_refresh", - "com.android.ims.ACTION_RCS_SERVICE_AVAILABLE", - "com.android.ims.ACTION_RCS_SERVICE_UNAVAILABLE", - "com.android.ims.ACTION_RCS_SERVICE_DIED", - "com.android.ims.ACTION_PRESENCE_CHANGED", - "com.android.ims.ACTION_PUBLISH_STATUS_CHANGED", - "com.android.ims.IMS_SERVICE_UP", - "com.android.ims.IMS_SERVICE_DOWN", - "com.android.ims.IMS_INCOMING_CALL", - "com.android.ims.internal.uce.UCE_SERVICE_UP", - "com.android.ims.internal.uce.UCE_SERVICE_DOWN", - "com.android.imsconnection.DISCONNECTED", - "com.android.intent.action.IMS_FEATURE_CHANGED", - "com.android.intent.action.IMS_CONFIG_CHANGED", - "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR", - "com.android.phone.vvm.omtp.sms.REQUEST_SENT", - "com.android.phone.vvm.ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT", - "com.android.internal.telephony.CARRIER_VVM_PACKAGE_INSTALLED", - "com.android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO", - "com.android.internal.telephony.ACTION_CARRIER_CERTIFICATE_DOWNLOAD", - "com.android.internal.telephony.action.COUNTRY_OVERRIDE", - "com.android.internal.telephony.OPEN_DEFAULT_SMS_APP", - "com.android.internal.telephony.ACTION_TEST_OVERRIDE_CARRIER_ID", - "android.telephony.action.SIM_CARD_STATE_CHANGED", - "android.telephony.action.SIM_APPLICATION_STATE_CHANGED", - "android.telephony.action.SIM_SLOT_STATUS_CHANGED", - "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED", - "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED", - "android.telephony.action.TOGGLE_PROVISION", - "android.telephony.action.NETWORK_COUNTRY_CHANGED", - "android.telephony.action.PRIMARY_SUBSCRIPTION_LIST_CHANGED", - "android.telephony.action.MULTI_SIM_CONFIG_CHANGED", - "android.telephony.action.CARRIER_SIGNAL_RESET", - "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE", - "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE", - "android.telephony.action.CARRIER_SIGNAL_REDIRECTED", - "android.telephony.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED", - "com.android.phone.settings.CARRIER_PROVISIONING", - "com.android.phone.settings.TRIGGER_CARRIER_PROVISIONING", - "com.android.internal.telephony.ACTION_VOWIFI_ENABLED", - "android.telephony.action.ANOMALY_REPORTED", - "android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED", - "android.intent.action.ACTION_MANAGED_ROAMING_IND", - "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE", - "android.safetycenter.action.REFRESH_SAFETY_SOURCES", - "android.safetycenter.action.SAFETY_CENTER_ENABLED_CHANGED", - "android.app.action.DEVICE_POLICY_RESOURCE_UPDATED", - "android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER", - "android.service.autofill.action.DELAYED_FILL", - "android.app.action.PROVISIONING_COMPLETED", - "android.app.action.LOST_MODE_LOCATION_UPDATE", - "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED", - "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT", - "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED", - "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED", - "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED", - "android.bluetooth.headsetclient.profile.action.AG_EVENT", - "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED", - "android.bluetooth.headsetclient.profile.action.RESULT", - "android.bluetooth.headsetclient.profile.action.LAST_VTAG", - "android.bluetooth.headsetclient.profile.action.NETWORK_SERVICE_STATE_CHANGED", - "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED", - "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED", - "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED", - "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED", - "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED", - "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED", - "android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED", - "android.bluetooth.avrcp-controller.profile.action.BROWSE_CONNECTION_STATE_CHANGED", - "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST", - "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT", - "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED", - "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED", - "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS", - "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED", - "com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT", - "com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY", - "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED", - "android.bluetooth.action.TETHERING_STATE_CHANGED", - "com.android.internal.action.EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS", - "android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED", - "com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION", - "com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" - ) - } -} diff --git a/tools/lint/framework/checks/src/test/java/com/google/android/lint/RegisterReceiverFlagDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/RegisterReceiverFlagDetectorTest.kt deleted file mode 100644 index 7c0ebca4ec1e..000000000000 --- a/tools/lint/framework/checks/src/test/java/com/google/android/lint/RegisterReceiverFlagDetectorTest.kt +++ /dev/null @@ -1,569 +0,0 @@ -/* - * Copyright (C) 2021 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.google.android.lint - -import com.android.tools.lint.checks.infrastructure.LintDetectorTest -import com.android.tools.lint.checks.infrastructure.TestFile -import com.android.tools.lint.checks.infrastructure.TestLintTask -import com.android.tools.lint.detector.api.Detector -import com.android.tools.lint.detector.api.Issue - -@Suppress("UnstableApiUsage") -class RegisterReceiverFlagDetectorTest : LintDetectorTest() { - - override fun getDetector(): Detector = RegisterReceiverFlagDetector() - - override fun getIssues(): List<Issue> = listOf( - RegisterReceiverFlagDetector.ISSUE_RECEIVER_EXPORTED_FLAG - ) - - override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) - - fun testProtectedBroadcast() { - lint().files( - java( - """ - package test.pkg; - import android.content.BroadcastReceiver; - import android.content.Context; - import android.content.Intent; - import android.content.IntentFilter; - public class TestClass1 { - public void testMethod(Context context, BroadcastReceiver receiver) { - IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); - context.registerReceiver(receiver, filter); - } - } - """ - ).indented(), - *stubs - ) - .run() - .expectClean() - } - - fun testProtectedBroadcastCreate() { - lint().files( - java( - """ - package test.pkg; - import android.content.BroadcastReceiver; - import android.content.Context; - import android.content.Intent; - import android.content.IntentFilter; - public class TestClass1 { - public void testMethod(Context context, BroadcastReceiver receiver) { - IntentFilter filter = - IntentFilter.create(Intent.ACTION_BATTERY_CHANGED, "foo/bar"); - context.registerReceiver(receiver, filter); - } - } - """ - ).indented(), - *stubs - ) - .run() - .expectClean() - } - - fun testMultipleProtectedBroadcasts() { - lint().files( - java( - """ - package test.pkg; - import android.content.BroadcastReceiver; - import android.content.Context; - import android.content.Intent; - import android.content.IntentFilter; - public class TestClass1 { - public void testMethod(Context context, BroadcastReceiver receiver) { - IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); - filter.addAction(Intent.ACTION_BATTERY_LOW); - filter.addAction(Intent.ACTION_BATTERY_OKAY); - context.registerReceiver(receiver, filter); - } - } - """ - ).indented(), - *stubs - ) - .run() - .expectClean() - } - - // TODO(b/267510341): Reenable this test - // fun testSubsequentFilterModification() { - // lint().files( - // java( - // """ - // package test.pkg; - // import android.content.BroadcastReceiver; - // import android.content.Context; - // import android.content.Intent; - // import android.content.IntentFilter; - // public class TestClass1 { - // public void testMethod(Context context, BroadcastReceiver receiver) { - // IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); - // filter.addAction(Intent.ACTION_BATTERY_LOW); - // filter.addAction(Intent.ACTION_BATTERY_OKAY); - // context.registerReceiver(receiver, filter); - // filter.addAction("querty"); - // context.registerReceiver(receiver, filter); - // } - // } - // """ - // ).indented(), - // *stubs - // ) - // .run() - // .expect(""" - // src/test/pkg/TestClass1.java:13: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag] - // context.registerReceiver(receiver, filter); - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // 0 errors, 1 warnings - // """.trimIndent()) - // } - - fun testNullReceiver() { - lint().files( - java( - """ - package test.pkg; - import android.content.BroadcastReceiver; - import android.content.Context; - import android.content.Intent; - import android.content.IntentFilter; - public class TestClass1 { - public void testMethod(Context context) { - IntentFilter filter = new IntentFilter("qwerty"); - context.registerReceiver(null, filter); - } - } - """ - ).indented(), - *stubs - ) - .run() - .expectClean() - } - - fun testExportedFlagPresent() { - lint().files( - java( - """ - package test.pkg; - import android.content.BroadcastReceiver; - import android.content.Context; - import android.content.Intent; - import android.content.IntentFilter; - public class TestClass1 { - public void testMethod(Context context, BroadcastReceiver receiver) { - IntentFilter filter = new IntentFilter("qwerty"); - context.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED); - } - } - """ - ).indented(), - *stubs - ) - .run() - .expectClean() - } - - fun testNotExportedFlagPresent() { - lint().files( - java( - """ - package test.pkg; - import android.content.BroadcastReceiver; - import android.content.Context; - import android.content.Intent; - import android.content.IntentFilter; - public class TestClass1 { - public void testMethod(Context context, BroadcastReceiver receiver) { - IntentFilter filter = new IntentFilter("qwerty"); - context.registerReceiver(receiver, filter, - Context.RECEIVER_NOT_EXPORTED); - } - } - """ - ).indented(), - *stubs - ) - .run() - .expectClean() - } - - // TODO(b/267510341): Reenable this test - // fun testFlagArgumentAbsent() { - // lint().files( - // java( - // """ - // package test.pkg; - // import android.content.BroadcastReceiver; - // import android.content.Context; - // import android.content.Intent; - // import android.content.IntentFilter; - // public class TestClass1 { - // public void testMethod(Context context, BroadcastReceiver receiver) { - // IntentFilter filter = new IntentFilter("qwerty"); - // context.registerReceiver(receiver, filter); - // } - // } - // """ - // ).indented(), - // *stubs - // ) - // .run() - // .expect(""" - // src/test/pkg/TestClass1.java:9: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag] - // context.registerReceiver(receiver, filter); - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // 0 errors, 1 warnings - // """.trimIndent()) - // } - - // TODO(b/267510341): Reenable this test - // fun testExportedFlagsAbsent() { - // lint().files( - // java( - // """ - // package test.pkg; - // import android.content.BroadcastReceiver; - // import android.content.Context; - // import android.content.Intent; - // import android.content.IntentFilter; - // public class TestClass1 { - // public void testMethod(Context context, BroadcastReceiver receiver) { - // IntentFilter filter = new IntentFilter("qwerty"); - // context.registerReceiver(receiver, filter, 0); - // } - // } - // """ - // ).indented(), - // *stubs - // ) - // .run() - // .expect(""" - // src/test/pkg/TestClass1.java:9: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag] - // context.registerReceiver(receiver, filter, 0); - // ~ - // 0 errors, 1 warnings - // """.trimIndent()) - // } - - fun testExportedFlagVariable() { - lint().files( - java( - """ - package test.pkg; - import android.content.BroadcastReceiver; - import android.content.Context; - import android.content.Intent; - import android.content.IntentFilter; - public class TestClass1 { - public void testMethod(Context context, BroadcastReceiver receiver) { - IntentFilter filter = new IntentFilter("qwerty"); - var flags = Context.RECEIVER_EXPORTED; - context.registerReceiver(receiver, filter, flags); - } - } - """ - ).indented(), - *stubs - ) - .run() - .expectClean() - } - - // TODO(b/267510341): Reenable this test - // fun testUnknownFilter() { - // lint().files( - // java( - // """ - // package test.pkg; - // import android.content.BroadcastReceiver; - // import android.content.Context; - // import android.content.Intent; - // import android.content.IntentFilter; - // public class TestClass1 { - // public void testMethod(Context context, BroadcastReceiver receiver, - // IntentFilter filter) { - // context.registerReceiver(receiver, filter); - // } - // } - // """ - // ).indented(), - // *stubs - // ) - // .run() - // .expect(""" - // src/test/pkg/TestClass1.java:9: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag] - // context.registerReceiver(receiver, filter); - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // 0 errors, 1 warnings - // """.trimIndent()) - // } - - // TODO(b/267510341): Reenable this test - // fun testFilterEscapes() { - // lint().files( - // java( - // """ - // package test.pkg; - // import android.content.BroadcastReceiver; - // import android.content.Context; - // import android.content.Intent; - // import android.content.IntentFilter; - // public class TestClass1 { - // public void testMethod(Context context, BroadcastReceiver receiver) { - // IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); - // updateFilter(filter); - // context.registerReceiver(receiver, filter); - // } - // } - // """ - // ).indented(), - // *stubs - // ) - // .run() - // .expect(""" - // src/test/pkg/TestClass1.java:10: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag] - // context.registerReceiver(receiver, filter); - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // 0 errors, 1 warnings - // """.trimIndent()) - // } - - fun testInlineFilter() { - lint().files( - java( - """ - package test.pkg; - import android.content.BroadcastReceiver; - import android.content.Context; - import android.content.Intent; - import android.content.IntentFilter; - public class TestClass1 { - public void testMethod(Context context, BroadcastReceiver receiver) { - context.registerReceiver(receiver, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - } - } - """ - ).indented(), - *stubs - ) - .run() - .expectClean() - } - - // TODO(b/267510341): Reenable this test - // fun testInlineFilterApply() { - // lint().files( - // kotlin( - // """ - // package test.pkg - // import android.content.BroadcastReceiver - // import android.content.Context - // import android.content.Intent - // import android.content.IntentFilter - // class TestClass1 { - // fun test(context: Context, receiver: BroadcastReceiver) { - // context.registerReceiver(receiver, - // IntentFilter(Intent.ACTION_BATTERY_CHANGED).apply { - // addAction("qwerty") - // }) - // } - // } - // """ - // ).indented(), - // *stubs - // ) - // .run() - // .expect(""" - // src/test/pkg/TestClass1.kt:8: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag] - // context.registerReceiver(receiver, - // ^ - // 0 errors, 1 warnings - // """.trimIndent()) - // } - - // TODO(b/267510341): Reenable this test - // fun testFilterVariableApply() { - // lint().files( - // kotlin( - // """ - // package test.pkg - // import android.content.BroadcastReceiver - // import android.content.Context - // import android.content.Intent - // import android.content.IntentFilter - // class TestClass1 { - // fun test(context: Context, receiver: BroadcastReceiver) { - // val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED).apply { - // addAction("qwerty") - // } - // context.registerReceiver(receiver, filter) - // } - // } - // """ - // ).indented(), - // *stubs - // ) - // .run() - // .expect(""" - // src/test/pkg/TestClass1.kt:11: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag] - // context.registerReceiver(receiver, filter) - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // 0 errors, 1 warnings - // """.trimIndent()) - // } - - // TODO(b/267510341): Reenable this test - // fun testFilterVariableApply2() { - // lint().files( - // kotlin( - // """ - // package test.pkg - // import android.content.BroadcastReceiver - // import android.content.Context - // import android.content.Intent - // import android.content.IntentFilter - // class TestClass1 { - // fun test(context: Context, receiver: BroadcastReceiver) { - // val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED).apply { - // addAction(Intent.ACTION_BATTERY_OKAY) - // } - // context.registerReceiver(receiver, filter.apply { - // addAction("qwerty") - // }) - // } - // } - // """ - // ).indented(), - // *stubs - // ) - // .run() - // .expect(""" - // src/test/pkg/TestClass1.kt:11: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag] - // context.registerReceiver(receiver, filter.apply { - // ^ - // 0 errors, 1 warnings - // """.trimIndent()) - // } - - // TODO(b/267510341): Reenable this test - // fun testFilterComplexChain() { - // lint().files( - // kotlin( - // """ - // package test.pkg - // import android.content.BroadcastReceiver - // import android.content.Context - // import android.content.Intent - // import android.content.IntentFilter - // class TestClass1 { - // fun test(context: Context, receiver: BroadcastReceiver) { - // val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED).apply { - // addAction(Intent.ACTION_BATTERY_OKAY) - // } - // val filter2 = filter - // val filter3 = filter2.apply { - // addAction(Intent.ACTION_BATTERY_LOW) - // } - // context.registerReceiver(receiver, filter3) - // val filter4 = filter3.apply { - // addAction("qwerty") - // } - // context.registerReceiver(receiver, filter4) - // } - // } - // """ - // ).indented(), - // *stubs - // ) - // .run() - // .expect(""" - // src/test/pkg/TestClass1.kt:19: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag] - // context.registerReceiver(receiver, filter4) - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // 0 errors, 1 warnings - // """.trimIndent()) - // } - - private val broadcastReceiverStub: TestFile = java( - """ - package android.content; - public class BroadcastReceiver { - // Stub - } - """ - ).indented() - - private val contextStub: TestFile = java( - """ - package android.content; - public class Context { - public static final int RECEIVER_EXPORTED = 0x2; - public static final int RECEIVER_NOT_EXPORTED = 0x4; - @Nullable - public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver, - IntentFilter filter, - @RegisterReceiverFlags int flags); - } - """ - ).indented() - - private val intentStub: TestFile = java( - """ - package android.content; - public class Intent { - public static final String ACTION_BATTERY_CHANGED = - "android.intent.action.BATTERY_CHANGED"; - public static final String ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW"; - public static final String ACTION_BATTERY_OKAY = - "android.intent.action.BATTERY_OKAY"; - } - """ - ).indented() - - private val intentFilterStub: TestFile = java( - """ - package android.content; - public class IntentFilter { - public IntentFilter() { - // Stub - } - public IntentFilter(String action) { - // Stub - } - public IntentFilter(String action, String dataType) { - // Stub - } - public static IntentFilter create(String action, String dataType) { - return null; - } - public final void addAction(String action) { - // Stub - } - } - """ - ).indented() - - private val stubs = arrayOf(broadcastReceiverStub, contextStub, intentStub, intentFilterStub) -} 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 9bfeb6308d13..fe397d9c2662 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.wifi.sharedconnectivity.service.SharedConnectivityService; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; @@ -86,6 +87,7 @@ public final class HotspotNetwork implements Parcelable { @Nullable @SecurityType private final ArraySet<Integer> mHotspotSecurityTypes; + private final Bundle mExtras; /** * Builder class for {@link HotspotNetwork}. @@ -102,8 +104,8 @@ public final class HotspotNetwork implements Parcelable { private String mHotspotBssid; @Nullable @SecurityType - private final ArraySet<Integer> mHotspotSecurityTypes = - new ArraySet<>(); + private final ArraySet<Integer> mHotspotSecurityTypes = new ArraySet<>(); + private Bundle mExtras = Bundle.EMPTY; /** * Set the remote device ID. @@ -190,6 +192,17 @@ public final class HotspotNetwork implements Parcelable { } /** + * Sets the extras bundle + * + * @return Returns the Builder object. + */ + @NonNull + public Builder setExtras(@NonNull Bundle extras) { + mExtras = extras; + return this; + } + + /** * Builds the {@link HotspotNetwork} object. * * @return Returns the built {@link HotspotNetwork} object. @@ -203,7 +216,8 @@ public final class HotspotNetwork implements Parcelable { mNetworkName, mHotspotSsid, mHotspotBssid, - mHotspotSecurityTypes); + mHotspotSecurityTypes, + mExtras); } } @@ -231,7 +245,8 @@ public final class HotspotNetwork implements Parcelable { @NonNull String networkName, @Nullable String hotspotSsid, @Nullable String hotspotBssid, - @Nullable @SecurityType ArraySet<Integer> hotspotSecurityTypes) { + @Nullable @SecurityType ArraySet<Integer> hotspotSecurityTypes, + @NonNull Bundle extras) { validate(deviceId, networkType, networkName, @@ -243,6 +258,7 @@ public final class HotspotNetwork implements Parcelable { mHotspotSsid = hotspotSsid; mHotspotBssid = hotspotBssid; mHotspotSecurityTypes = new ArraySet<>(hotspotSecurityTypes); + mExtras = extras; } /** @@ -315,6 +331,16 @@ public final class HotspotNetwork implements Parcelable { return mHotspotSecurityTypes; } + /** + * Gets the extras Bundle. + * + * @return Returns a Bundle object. + */ + @NonNull + public Bundle getExtras() { + return mExtras; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof HotspotNetwork)) return false; @@ -348,6 +374,7 @@ public final class HotspotNetwork implements Parcelable { dest.writeString(mHotspotSsid); dest.writeString(mHotspotBssid); dest.writeArraySet(mHotspotSecurityTypes); + dest.writeBundle(mExtras); } /** @@ -359,7 +386,7 @@ public final class HotspotNetwork implements Parcelable { public static HotspotNetwork readFromParcel(@NonNull Parcel in) { return new HotspotNetwork(in.readLong(), NetworkProviderInfo.readFromParcel(in), in.readInt(), in.readString(), in.readString(), in.readString(), - (ArraySet<Integer>) in.readArraySet(null)); + (ArraySet<Integer>) in.readArraySet(null), in.readBundle()); } @NonNull @@ -385,6 +412,7 @@ public final class HotspotNetwork implements Parcelable { .append(", hotspotSsid=").append(mHotspotSsid) .append(", hotspotBssid=").append(mHotspotBssid) .append(", hotspotSecurityTypes=").append(mHotspotSecurityTypes.toString()) + .append(", extras=").append(mExtras.toString()) .append("]").toString(); } } diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java index 69767f302bd8..72acf2ccab0c 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java @@ -117,7 +117,7 @@ public final class HotspotNetworkConnectionStatus implements Parcelable { @ConnectionStatus private int mStatus; private HotspotNetwork mHotspotNetwork; - private Bundle mExtras; + private Bundle mExtras = Bundle.EMPTY; /** * Sets the status of the connection @@ -179,7 +179,7 @@ public final class HotspotNetworkConnectionStatus implements Parcelable { } private HotspotNetworkConnectionStatus(@ConnectionStatus int status, - HotspotNetwork hotspotNetwork, Bundle extras) { + HotspotNetwork hotspotNetwork, @NonNull Bundle extras) { validate(status); mStatus = status; mHotspotNetwork = hotspotNetwork; diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java index 64412bc55fb2..c390e42f348e 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java @@ -22,6 +22,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -73,6 +74,7 @@ public final class KnownNetwork implements Parcelable { @SecurityType private final ArraySet<Integer> mSecurityTypes; private final NetworkProviderInfo mNetworkProviderInfo; + private final Bundle mExtras; /** * Builder class for {@link KnownNetwork}. @@ -84,6 +86,7 @@ public final class KnownNetwork implements Parcelable { @SecurityType private final ArraySet<Integer> mSecurityTypes = new ArraySet<>(); private NetworkProviderInfo mNetworkProviderInfo; + private Bundle mExtras = Bundle.EMPTY; /** * Sets the indicated source of the known network. @@ -135,6 +138,17 @@ public final class KnownNetwork implements Parcelable { } /** + * Sets the extras bundle + * + * @return Returns the Builder object. + */ + @NonNull + public Builder setExtras(@NonNull Bundle extras) { + mExtras = extras; + return this; + } + + /** * Builds the {@link KnownNetwork} object. * * @return Returns the built {@link KnownNetwork} object. @@ -145,7 +159,8 @@ public final class KnownNetwork implements Parcelable { mNetworkSource, mSsid, mSecurityTypes, - mNetworkProviderInfo); + mNetworkProviderInfo, + mExtras); } } @@ -173,12 +188,14 @@ public final class KnownNetwork implements Parcelable { @NetworkSource int networkSource, @NonNull String ssid, @NonNull @SecurityType ArraySet<Integer> securityTypes, - @Nullable NetworkProviderInfo networkProviderInfo) { + @Nullable NetworkProviderInfo networkProviderInfo, + @NonNull Bundle extras) { validate(networkSource, ssid, securityTypes, networkProviderInfo); mNetworkSource = networkSource; mSsid = ssid; mSecurityTypes = new ArraySet<>(securityTypes); mNetworkProviderInfo = networkProviderInfo; + mExtras = extras; } /** @@ -223,6 +240,16 @@ public final class KnownNetwork implements Parcelable { return mNetworkProviderInfo; } + /** + * Gets the extras Bundle. + * + * @return Returns a Bundle object. + */ + @NonNull + public Bundle getExtras() { + return mExtras; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof KnownNetwork)) return false; @@ -249,6 +276,7 @@ public final class KnownNetwork implements Parcelable { dest.writeString(mSsid); dest.writeArraySet(mSecurityTypes); mNetworkProviderInfo.writeToParcel(dest, flags); + dest.writeBundle(mExtras); } /** @@ -260,7 +288,7 @@ public final class KnownNetwork implements Parcelable { public static KnownNetwork readFromParcel(@NonNull Parcel in) { return new KnownNetwork(in.readInt(), in.readString(), (ArraySet<Integer>) in.readArraySet(null), - NetworkProviderInfo.readFromParcel(in)); + NetworkProviderInfo.readFromParcel(in), in.readBundle()); } @NonNull @@ -283,6 +311,7 @@ public final class KnownNetwork implements Parcelable { .append(", ssid=").append(mSsid) .append(", securityTypes=").append(mSecurityTypes.toString()) .append(", networkProviderInfo=").append(mNetworkProviderInfo.toString()) + .append(", extras=").append(mExtras.toString()) .append("]").toString(); } } diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatus.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatus.java index 6bd0a5ecd4c8..b30dc3f6b530 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatus.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatus.java @@ -72,7 +72,7 @@ public final class KnownNetworkConnectionStatus implements Parcelable { public static final class Builder { @ConnectionStatus private int mStatus; private KnownNetwork mKnownNetwork; - private Bundle mExtras; + private Bundle mExtras = Bundle.EMPTY; public Builder() {} @@ -128,7 +128,7 @@ public final class KnownNetworkConnectionStatus implements Parcelable { } private KnownNetworkConnectionStatus(@ConnectionStatus int status, KnownNetwork knownNetwork, - Bundle extras) { + @NonNull Bundle extras) { validate(status); mStatus = status; mKnownNetwork = knownNetwork; diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java index ed4d699ad4de..25fbabce71ae 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java @@ -21,6 +21,7 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.net.wifi.sharedconnectivity.service.SharedConnectivityService; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -89,6 +90,7 @@ public final class NetworkProviderInfo implements Parcelable { private final String mModelName; private final int mBatteryPercentage; private final int mConnectionStrength; + private final Bundle mExtras; /** * Builder class for {@link NetworkProviderInfo}. @@ -99,6 +101,7 @@ public final class NetworkProviderInfo implements Parcelable { private String mModelName; private int mBatteryPercentage; private int mConnectionStrength; + private Bundle mExtras = Bundle.EMPTY; public Builder(@NonNull String deviceName, @NonNull String modelName) { Objects.requireNonNull(deviceName); @@ -170,6 +173,17 @@ public final class NetworkProviderInfo implements Parcelable { } /** + * Sets the extras bundle + * + * @return Returns the Builder object. + */ + @NonNull + public Builder setExtras(@NonNull Bundle extras) { + mExtras = extras; + return this; + } + + /** * Builds the {@link NetworkProviderInfo} object. * * @return Returns the built {@link NetworkProviderInfo} object. @@ -177,7 +191,7 @@ public final class NetworkProviderInfo implements Parcelable { @NonNull public NetworkProviderInfo build() { return new NetworkProviderInfo(mDeviceType, mDeviceName, mModelName, mBatteryPercentage, - mConnectionStrength); + mConnectionStrength, mExtras); } } @@ -197,13 +211,15 @@ public final class NetworkProviderInfo implements Parcelable { } private NetworkProviderInfo(@DeviceType int deviceType, @NonNull String deviceName, - @NonNull String modelName, int batteryPercentage, int connectionStrength) { + @NonNull String modelName, int batteryPercentage, int connectionStrength, + @NonNull Bundle extras) { validate(deviceType, deviceName, modelName, batteryPercentage, connectionStrength); mDeviceType = deviceType; mDeviceName = deviceName; mModelName = modelName; mBatteryPercentage = batteryPercentage; mConnectionStrength = connectionStrength; + mExtras = extras; } /** @@ -256,6 +272,16 @@ public final class NetworkProviderInfo implements Parcelable { return mConnectionStrength; } + /** + * Gets the extras Bundle. + * + * @return Returns a Bundle object. + */ + @NonNull + public Bundle getExtras() { + return mExtras; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof NetworkProviderInfo)) return false; @@ -280,6 +306,7 @@ public final class NetworkProviderInfo implements Parcelable { dest.writeString(mModelName); dest.writeInt(mBatteryPercentage); dest.writeInt(mConnectionStrength); + dest.writeBundle(mExtras); } @Override @@ -295,7 +322,7 @@ public final class NetworkProviderInfo implements Parcelable { @NonNull public static NetworkProviderInfo readFromParcel(@NonNull Parcel in) { return new NetworkProviderInfo(in.readInt(), in.readString(), in.readString(), in.readInt(), - in.readInt()); + in.readInt(), in.readBundle()); } @NonNull @@ -319,6 +346,7 @@ public final class NetworkProviderInfo implements Parcelable { .append(", modelName=").append(mModelName) .append(", batteryPercentage=").append(mBatteryPercentage) .append(", connectionStrength=").append(mConnectionStrength) + .append(", extras=").append(mExtras.toString()) .append("]").toString(); } } diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java index 63e471b25ffb..30bb98962b3c 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java @@ -51,7 +51,7 @@ public final class SharedConnectivitySettingsState implements Parcelable { private boolean mInstantTetherEnabled; private Intent mInstantTetherSettingsIntent; private final Context mContext; - private Bundle mExtras; + private Bundle mExtras = Bundle.EMPTY; public Builder(@NonNull Context context) { mContext = context; @@ -112,8 +112,7 @@ public final class SharedConnectivitySettingsState implements Parcelable { } private SharedConnectivitySettingsState(boolean instantTetherEnabled, - PendingIntent pendingIntent, Bundle extras) { - + PendingIntent pendingIntent, @NonNull Bundle extras) { mInstantTetherEnabled = instantTetherEnabled; mInstantTetherSettingsPendingIntent = pendingIntent; mExtras = extras; 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 8302094b1c34..0827ffaea482 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java @@ -28,6 +28,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; +import android.os.Bundle; import android.os.Parcel; import android.util.ArraySet; @@ -55,6 +56,8 @@ public class HotspotNetworkTest { private static final String HOTSPOT_SSID = "TEST_SSID"; private static final String HOTSPOT_BSSID = "TEST _BSSID"; private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP}; + private static final String BUNDLE_KEY = "INT-KEY"; + private static final int BUNDLE_VALUE = 1; private static final long DEVICE_ID_1 = 111L; private static final NetworkProviderInfo NETWORK_PROVIDER_INFO1 = @@ -138,6 +141,7 @@ public class HotspotNetworkTest { assertThat(network.getHotspotSsid()).isEqualTo(HOTSPOT_SSID); assertThat(network.getHotspotBssid()).isEqualTo(HOTSPOT_BSSID); assertThat(network.getHotspotSecurityTypes()).containsExactlyElementsIn(securityTypes); + assertThat(network.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE); } @Test @@ -161,11 +165,18 @@ public class HotspotNetworkTest { .setHostNetworkType(NETWORK_TYPE) .setNetworkName(NETWORK_NAME) .setHotspotSsid(HOTSPOT_SSID) - .setHotspotBssid(HOTSPOT_BSSID); + .setHotspotBssid(HOTSPOT_BSSID) + .setExtras(buildBundle()); Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType); if (withNetworkProviderInfo) { builder.setNetworkProviderInfo(NETWORK_PROVIDER_INFO); } return builder; } + + private Bundle buildBundle() { + Bundle bundle = new Bundle(); + bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE); + return bundle; + } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java index 1ecba7644cf9..81d7b44382e0 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java @@ -25,6 +25,7 @@ import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE import static com.google.common.truth.Truth.assertThat; +import android.os.Bundle; import android.os.Parcel; import android.util.ArraySet; @@ -47,6 +48,9 @@ public class KnownNetworkTest { new NetworkProviderInfo.Builder("TEST_NAME", "TEST_MODEL") .setDeviceType(DEVICE_TYPE_TABLET).setConnectionStrength(2) .setBatteryPercentage(50).build(); + private static final String BUNDLE_KEY = "INT-KEY"; + private static final int BUNDLE_VALUE = 1; + private static final int NETWORK_SOURCE_1 = NETWORK_SOURCE_CLOUD_SELF; private static final String SSID_1 = "TEST_SSID1"; private static final int[] SECURITY_TYPES_1 = {SECURITY_TYPE_PSK}; @@ -113,6 +117,7 @@ public class KnownNetworkTest { assertThat(network.getSsid()).isEqualTo(SSID); assertThat(network.getSecurityTypes()).containsExactlyElementsIn(securityTypes); assertThat(network.getNetworkProviderInfo()).isEqualTo(NETWORK_PROVIDER_INFO); + assertThat(network.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE); } @Test @@ -125,8 +130,15 @@ public class KnownNetworkTest { private KnownNetwork.Builder buildKnownNetworkBuilder() { KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE) - .setSsid(SSID).setNetworkProviderInfo(NETWORK_PROVIDER_INFO); + .setSsid(SSID).setNetworkProviderInfo(NETWORK_PROVIDER_INFO) + .setExtras(buildBundle()); Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType); return builder; } + + private Bundle buildBundle() { + Bundle bundle = new Bundle(); + bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE); + return bundle; + } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java index 8f35d8d94a8b..4aa9ca684ad0 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java @@ -21,6 +21,7 @@ import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE import static com.google.common.truth.Truth.assertThat; +import android.os.Bundle; import android.os.Parcel; import androidx.test.filters.SmallTest; @@ -38,6 +39,8 @@ public class NetworkProviderInfoTest { private static final String DEVICE_MODEL = "TEST_MODEL"; private static final int BATTERY_PERCENTAGE = 50; private static final int CONNECTION_STRENGTH = 2; + private static final String BUNDLE_KEY = "INT-KEY"; + private static final int BUNDLE_VALUE = 1; private static final int DEVICE_TYPE_1 = DEVICE_TYPE_LAPTOP; private static final String DEVICE_NAME_1 = "TEST_NAME1"; @@ -105,6 +108,7 @@ public class NetworkProviderInfoTest { assertThat(info.getModelName()).isEqualTo(DEVICE_MODEL); assertThat(info.getBatteryPercentage()).isEqualTo(BATTERY_PERCENTAGE); assertThat(info.getConnectionStrength()).isEqualTo(CONNECTION_STRENGTH); + assertThat(info.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE); } @Test @@ -118,6 +122,13 @@ public class NetworkProviderInfoTest { private NetworkProviderInfo.Builder buildNetworkProviderInfoBuilder() { return new NetworkProviderInfo.Builder(DEVICE_NAME, DEVICE_MODEL).setDeviceType(DEVICE_TYPE) .setBatteryPercentage(BATTERY_PERCENTAGE) - .setConnectionStrength(CONNECTION_STRENGTH); + .setConnectionStrength(CONNECTION_STRENGTH) + .setExtras(buildBundle()); + } + + private Bundle buildBundle() { + Bundle bundle = new Bundle(); + bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE); + return bundle; } } |