diff options
193 files changed, 3168 insertions, 1313 deletions
diff --git a/Android.bp b/Android.bp index 59e903ef37d3..f6bfe6545284 100644 --- a/Android.bp +++ b/Android.bp @@ -389,7 +389,6 @@ java_defaults { // TODO(b/120066492): remove gps_debug and protolog.conf.json when the build // system propagates "required" properly. "gps_debug.conf", - "core.protolog.pb", "framework-res", // any install dependencies should go into framework-minus-apex-install-dependencies // rather than here to avoid bloating incremental build time diff --git a/Ravenwood.bp b/Ravenwood.bp index f43c37bf637d..c3b22c4f4331 100644 --- a/Ravenwood.bp +++ b/Ravenwood.bp @@ -30,7 +30,7 @@ java_genrule { name: "framework-minus-apex.ravenwood-base", tools: ["hoststubgen"], cmd: "$(location hoststubgen) " + - "@$(location ravenwood/ravenwood-standard-options.txt) " + + "@$(location ravenwood/texts/ravenwood-standard-options.txt) " + "--debug-log $(location hoststubgen_framework-minus-apex.log) " + "--stats-file $(location hoststubgen_framework-minus-apex_stats.csv) " + @@ -41,13 +41,13 @@ java_genrule { "--gen-input-dump-file $(location hoststubgen_dump.txt) " + "--in-jar $(location :framework-minus-apex-for-hoststubgen) " + - "--policy-override-file $(location ravenwood/framework-minus-apex-ravenwood-policies.txt) " + - "--annotation-allowed-classes-file $(location ravenwood/ravenwood-annotation-allowed-classes.txt) ", + "--policy-override-file $(location ravenwood/texts/framework-minus-apex-ravenwood-policies.txt) " + + "--annotation-allowed-classes-file $(location ravenwood/texts/ravenwood-annotation-allowed-classes.txt) ", srcs: [ ":framework-minus-apex-for-hoststubgen", - "ravenwood/framework-minus-apex-ravenwood-policies.txt", - "ravenwood/ravenwood-standard-options.txt", - "ravenwood/ravenwood-annotation-allowed-classes.txt", + "ravenwood/texts/framework-minus-apex-ravenwood-policies.txt", + "ravenwood/texts/ravenwood-standard-options.txt", + "ravenwood/texts/ravenwood-annotation-allowed-classes.txt", ], out: [ "ravenwood.jar", @@ -77,6 +77,19 @@ java_genrule { ], } +// Extract the stats file. +genrule { + name: "framework-minus-apex.ravenwood.stats", + defaults: ["ravenwood-internal-only-visibility-genrule"], + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-minus-apex.ravenwood-base{hoststubgen_framework-minus-apex_stats.csv}", + ], + out: [ + "hoststubgen_framework-minus-apex_stats.csv", + ], +} + java_library { name: "services.core-for-hoststubgen", installable: false, // host only jar. @@ -91,7 +104,7 @@ java_genrule { name: "services.core.ravenwood-base", tools: ["hoststubgen"], cmd: "$(location hoststubgen) " + - "@$(location ravenwood/ravenwood-standard-options.txt) " + + "@$(location ravenwood/texts/ravenwood-standard-options.txt) " + "--debug-log $(location hoststubgen_services.core.log) " + "--stats-file $(location hoststubgen_services.core_stats.csv) " + @@ -102,13 +115,13 @@ java_genrule { "--gen-input-dump-file $(location hoststubgen_dump.txt) " + "--in-jar $(location :services.core-for-hoststubgen) " + - "--policy-override-file $(location ravenwood/services.core-ravenwood-policies.txt) " + - "--annotation-allowed-classes-file $(location ravenwood/ravenwood-annotation-allowed-classes.txt) ", + "--policy-override-file $(location ravenwood/texts/services.core-ravenwood-policies.txt) " + + "--annotation-allowed-classes-file $(location ravenwood/texts/ravenwood-annotation-allowed-classes.txt) ", srcs: [ ":services.core-for-hoststubgen", - "ravenwood/services.core-ravenwood-policies.txt", - "ravenwood/ravenwood-standard-options.txt", - "ravenwood/ravenwood-annotation-allowed-classes.txt", + "ravenwood/texts/services.core-ravenwood-policies.txt", + "ravenwood/texts/ravenwood-standard-options.txt", + "ravenwood/texts/ravenwood-annotation-allowed-classes.txt", ], out: [ "ravenwood.jar", @@ -135,6 +148,19 @@ java_genrule { ], } +// Extract the stats file. +genrule { + name: "services.core.ravenwood.stats", + defaults: ["ravenwood-internal-only-visibility-genrule"], + cmd: "cp $(in) $(out)", + srcs: [ + ":services.core.ravenwood-base{hoststubgen_services.core_stats.csv}", + ], + out: [ + "hoststubgen_services.core_stats.csv", + ], +} + java_library { name: "services.core.ravenwood-jarjar", installable: false, diff --git a/core/api/test-current.txt b/core/api/test-current.txt index ca5d3eb4a292..61897030a991 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3676,6 +3676,10 @@ package android.view { field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800 } + public final class PointerIcon implements android.os.Parcelable { + method @FlaggedApi("android.view.flags.enable_vector_cursors") public void setDrawNativeDropShadow(boolean); + } + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface RemotableViewMethod { method public abstract String asyncImpl() default ""; } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 5e6b54b1d0ea..84bc6cec7208 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -962,4 +962,12 @@ interface IActivityManager { */ oneway void frozenBinderTransactionDetected(int debugPid, int code, int flags, int err); int getBindingUidProcessState(int uid, in String callingPackage); + + /** + * Return the timestampe (in the elapsed timebase) when the UID became idle from active + * last time (regardless of if the UID is still idle, or became active again). + * This is useful when trying to detect whether an UID has ever became idle since a certain + * time in the past. + */ + long getUidLastIdleElapsedTime(int uid, in String callingPackage); } diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index be0aaff95c96..25697c5c662c 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -197,8 +197,8 @@ flag { description: "Fix provisioning for single-user headless DO" bug: "289515470" metadata { - purpose: PURPOSE_BUGFIX - } + purpose: PURPOSE_BUGFIX + } } flag { @@ -214,3 +214,13 @@ flag { description: "Guards a new flow for recursive required enterprise app list merging" bug: "319084618" } + +flag { + name: "headless_device_owner_delegate_security_logging_bug_fix" + namespace: "enterprise" + description: "Fix delegate security logging for single user headless DO." + bug: "289515470" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 2c26389071ce..a08d659ab4aa 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -26,6 +26,7 @@ import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -80,25 +81,27 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; /** - * System level service for managing companion devices + * Public interfaces for managing companion devices. * - * See <a href="{@docRoot}guide/topics/connectivity/companion-device-pairing">this guide</a> - * for a usage example. + * <p>The interfaces in this class allow companion apps to + * {@link #associate(AssociationRequest, Executor, Callback)} discover and request device profiles} + * for companion devices, {@link #startObservingDevicePresence(String) listen to device presence + * events}, {@link #startSystemDataTransfer(int, Executor, OutcomeReceiver) transfer system level + * data} via {@link #attachSystemDataTransport(int, InputStream, OutputStream) the reported + * channel} and more.</p> * - * <p>To obtain an instance call {@link Context#getSystemService}({@link - * Context#COMPANION_DEVICE_SERVICE}) Then, call {@link #associate(AssociationRequest, - * Callback, Handler)} to initiate the flow of associating current package with a - * device selected by user.</p> - * - * @see CompanionDeviceManager#associate - * @see AssociationRequest + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about managing companion devices, read the <a href= + * "{@docRoot}guide/topics/connectivity/companion-device-pairing">Companion Device Pairing</a> + * developer guide. + * </div> */ @SuppressLint("LongLogTag") @SystemService(Context.COMPANION_DEVICE_SERVICE) +@RequiresFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP) public final class CompanionDeviceManager { - - private static final boolean DEBUG = false; - private static final String LOG_TAG = "CDM_CompanionDeviceManager"; + private static final String TAG = "CDM_CompanionDeviceManager"; /** @hide */ @IntDef(prefix = {"RESULT_"}, value = { @@ -374,7 +377,7 @@ public final class CompanionDeviceManager { } private final ICompanionDeviceManager mService; - private Context mContext; + private final Context mContext; @GuardedBy("mListeners") private final ArrayList<OnAssociationsChangedListenerProxy> mListeners = new ArrayList<>(); @@ -432,7 +435,11 @@ public final class CompanionDeviceManager { @NonNull AssociationRequest request, @NonNull Callback callback, @Nullable Handler handler) { - if (!checkFeaturePresent()) return; + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + Objects.requireNonNull(request, "Request cannot be null"); Objects.requireNonNull(callback, "Callback cannot be null"); handler = Handler.mainIfNull(handler); @@ -492,7 +499,11 @@ public final class CompanionDeviceManager { @NonNull AssociationRequest request, @NonNull Executor executor, @NonNull Callback callback) { - if (!checkFeaturePresent()) return; + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + Objects.requireNonNull(request, "Request cannot be null"); Objects.requireNonNull(executor, "Executor cannot be null"); Objects.requireNonNull(callback, "Callback cannot be null"); @@ -521,7 +532,10 @@ public final class CompanionDeviceManager { @UserHandleAware @Nullable public IntentSender buildAssociationCancellationIntent() { - if (!checkFeaturePresent()) return null; + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return null; + } try { PendingIntent pendingIntent = mService.buildAssociationCancellationIntent( @@ -543,7 +557,8 @@ public final class CompanionDeviceManager { * @param flags system data types to be enabled. */ public void enableSystemDataSyncForTypes(int associationId, @DataSyncTypes int flags) { - if (!checkFeaturePresent()) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); return; } @@ -565,7 +580,8 @@ public final class CompanionDeviceManager { * @param flags system data types to be disabled. */ public void disableSystemDataSyncForTypes(int associationId, @DataSyncTypes int flags) { - if (!checkFeaturePresent()) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); return; } @@ -580,6 +596,11 @@ public final class CompanionDeviceManager { * @hide */ public void enablePermissionsSync(int associationId) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + try { mService.enablePermissionsSync(associationId); } catch (RemoteException e) { @@ -591,6 +612,11 @@ public final class CompanionDeviceManager { * @hide */ public void disablePermissionsSync(int associationId) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + try { mService.disablePermissionsSync(associationId); } catch (RemoteException e) { @@ -602,6 +628,11 @@ public final class CompanionDeviceManager { * @hide */ public PermissionSyncRequest getPermissionSyncRequest(int associationId) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return null; + } + try { return mService.getPermissionSyncRequest(associationId); } catch (RemoteException e) { @@ -636,7 +667,10 @@ public final class CompanionDeviceManager { @UserHandleAware @NonNull public List<AssociationInfo> getMyAssociations() { - if (!checkFeaturePresent()) return Collections.emptyList(); + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return Collections.emptyList(); + } try { return mService.getAssociations(mContext.getOpPackageName(), mContext.getUserId()); @@ -665,7 +699,10 @@ public final class CompanionDeviceManager { @UserHandleAware @Deprecated public void disassociate(@NonNull String deviceMacAddress) { - if (!checkFeaturePresent()) return; + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } try { mService.legacyDisassociate(deviceMacAddress, mContext.getOpPackageName(), @@ -690,7 +727,10 @@ public final class CompanionDeviceManager { */ @UserHandleAware public void disassociate(int associationId) { - if (!checkFeaturePresent()) return; + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } try { mService.disassociate(associationId); @@ -716,9 +756,11 @@ public final class CompanionDeviceManager { */ @UserHandleAware public void requestNotificationAccess(ComponentName component) { - if (!checkFeaturePresent()) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); return; } + try { PendingIntent pendingIntent = mService.requestNotificationAccess( component, mContext.getUserId()); @@ -755,9 +797,11 @@ public final class CompanionDeviceManager { */ @Deprecated public boolean hasNotificationAccess(ComponentName component) { - if (!checkFeaturePresent()) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); return false; } + try { return mService.hasNotificationAccess(component); } catch (RemoteException e) { @@ -793,7 +837,11 @@ public final class CompanionDeviceManager { @NonNull String packageName, @NonNull MacAddress macAddress, @NonNull UserHandle user) { - if (!checkFeaturePresent()) return false; + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return false; + } + Objects.requireNonNull(packageName, "package name cannot be null"); Objects.requireNonNull(macAddress, "mac address cannot be null"); Objects.requireNonNull(user, "user cannot be null"); @@ -816,7 +864,8 @@ public final class CompanionDeviceManager { @SystemApi @UserHandleAware @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) - public @NonNull List<AssociationInfo> getAllAssociations() { + @NonNull + public List<AssociationInfo> getAllAssociations() { return getAllAssociations(mContext.getUserId()); } @@ -826,8 +875,13 @@ public final class CompanionDeviceManager { * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) - public @NonNull List<AssociationInfo> getAllAssociations(@UserIdInt int userId) { - if (!checkFeaturePresent()) return Collections.emptyList(); + @NonNull + public List<AssociationInfo> getAllAssociations(@UserIdInt int userId) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return Collections.emptyList(); + } + try { return mService.getAllAssociationsForUser(userId); } catch (RemoteException e) { @@ -875,7 +929,11 @@ public final class CompanionDeviceManager { public void addOnAssociationsChangedListener( @NonNull Executor executor, @NonNull OnAssociationsChangedListener listener, @UserIdInt int userId) { - if (!checkFeaturePresent()) return; + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + synchronized (mListeners) { final OnAssociationsChangedListenerProxy proxy = new OnAssociationsChangedListenerProxy( executor, listener); @@ -899,7 +957,11 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) public void removeOnAssociationsChangedListener( @NonNull OnAssociationsChangedListener listener) { - if (!checkFeaturePresent()) return; + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + synchronized (mListeners) { final Iterator<OnAssociationsChangedListenerProxy> iterator = mListeners.iterator(); while (iterator.hasNext()) { @@ -931,6 +993,11 @@ public final class CompanionDeviceManager { public void addOnTransportsChangedListener( @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<List<AssociationInfo>> listener) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + final OnTransportsChangedListenerProxy proxy = new OnTransportsChangedListenerProxy( executor, listener); try { @@ -950,6 +1017,11 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void removeOnTransportsChangedListener( @NonNull Consumer<List<AssociationInfo>> listener) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + final OnTransportsChangedListenerProxy proxy = new OnTransportsChangedListenerProxy( null, listener); try { @@ -969,6 +1041,11 @@ public final class CompanionDeviceManager { */ @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void sendMessage(int messageType, @NonNull byte[] data, @NonNull int[] associationIds) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + try { mService.sendMessage(messageType, data, associationIds); } catch (RemoteException e) { @@ -989,6 +1066,11 @@ public final class CompanionDeviceManager { public void addOnMessageReceivedListener( @NonNull @CallbackExecutor Executor executor, int messageType, @NonNull BiConsumer<Integer, byte[]> listener) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + final OnMessageReceivedListenerProxy proxy = new OnMessageReceivedListenerProxy( executor, listener); try { @@ -1006,6 +1088,11 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void removeOnMessageReceivedListener(int messageType, @NonNull BiConsumer<Integer, byte[]> listener) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + final OnMessageReceivedListenerProxy proxy = new OnMessageReceivedListenerProxy( null, listener); try { @@ -1031,9 +1118,11 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) public boolean canPairWithoutPrompt(@NonNull String packageName, @NonNull String deviceMacAddress, @NonNull UserHandle user) { - if (!checkFeaturePresent()) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); return false; } + Objects.requireNonNull(packageName, "package name cannot be null"); Objects.requireNonNull(deviceMacAddress, "device mac address cannot be null"); Objects.requireNonNull(user, "user handle cannot be null"); @@ -1081,9 +1170,11 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String deviceAddress) throws DeviceNotAssociatedException { - if (!checkFeaturePresent()) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); return; } + Objects.requireNonNull(deviceAddress, "address cannot be null"); try { mService.legacyStartObservingDevicePresence(deviceAddress, @@ -1123,9 +1214,11 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String deviceAddress) throws DeviceNotAssociatedException { - if (!checkFeaturePresent()) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); return; } + Objects.requireNonNull(deviceAddress, "address cannot be null"); try { mService.legacyStopObservingDevicePresence(deviceAddress, @@ -1171,6 +1264,11 @@ public final class CompanionDeviceManager { @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull ObservingDevicePresenceRequest request) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + Objects.requireNonNull(request, "request cannot be null"); try { @@ -1192,6 +1290,11 @@ public final class CompanionDeviceManager { @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull ObservingDevicePresenceRequest request) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + Objects.requireNonNull(request, "request cannot be null"); try { @@ -1222,7 +1325,7 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void dispatchMessage(int messageId, int associationId, @NonNull byte[] message) throws DeviceNotAssociatedException { - Log.w(LOG_TAG, "dispatchMessage replaced by attachSystemDataTransport"); + Log.w(TAG, "dispatchMessage replaced by attachSystemDataTransport"); } /** @@ -1244,6 +1347,11 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void attachSystemDataTransport(int associationId, @NonNull InputStream in, @NonNull OutputStream out) throws DeviceNotAssociatedException { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + synchronized (mTransports) { if (mTransports.contains(associationId)) { detachSystemDataTransport(associationId); @@ -1272,6 +1380,11 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void detachSystemDataTransport(int associationId) throws DeviceNotAssociatedException { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + synchronized (mTransports) { final Transport transport = mTransports.get(associationId); if (transport != null) { @@ -1296,9 +1409,11 @@ public final class CompanionDeviceManager { @NonNull String packageName, @NonNull MacAddress macAddress, @NonNull byte[] certificate) { - if (!checkFeaturePresent()) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); return; } + Objects.requireNonNull(packageName, "package name cannot be null"); Objects.requireNonNull(macAddress, "mac address cannot be null"); @@ -1327,6 +1442,11 @@ public final class CompanionDeviceManager { @SystemApi @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceAppeared(int associationId) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + try { mService.notifySelfManagedDeviceAppeared(associationId); } catch (RemoteException e) { @@ -1349,6 +1469,11 @@ public final class CompanionDeviceManager { @SystemApi @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceDisappeared(int associationId) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + try { mService.notifySelfManagedDeviceDisappeared(associationId); } catch (RemoteException e) { @@ -1384,6 +1509,11 @@ public final class CompanionDeviceManager { @Nullable public IntentSender buildPermissionTransferUserConsentIntent(int associationId) throws DeviceNotAssociatedException { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return null; + } + try { PendingIntent pendingIntent = mService.buildPermissionTransferUserConsentIntent( mContext.getOpPackageName(), @@ -1420,6 +1550,11 @@ public final class CompanionDeviceManager { @UserHandleAware @FlaggedApi(Flags.FLAG_PERM_SYNC_USER_CONSENT) public boolean isPermissionTransferUserConsented(int associationId) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return false; + } + try { return mService.isPermissionTransferUserConsented(mContext.getOpPackageName(), mContext.getUserId(), associationId); @@ -1446,6 +1581,11 @@ public final class CompanionDeviceManager { @Deprecated @UserHandleAware public void startSystemDataTransfer(int associationId) throws DeviceNotAssociatedException { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + try { mService.startSystemDataTransfer(mContext.getOpPackageName(), mContext.getUserId(), associationId, null); @@ -1478,6 +1618,11 @@ public final class CompanionDeviceManager { @NonNull Executor executor, @NonNull OutcomeReceiver<Void, CompanionException> result) throws DeviceNotAssociatedException { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + try { mService.startSystemDataTransfer(mContext.getOpPackageName(), mContext.getUserId(), associationId, new SystemDataTransferCallbackProxy(executor, result)); @@ -1495,6 +1640,11 @@ public final class CompanionDeviceManager { */ @UserHandleAware public boolean isCompanionApplicationBound() { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return false; + } + try { return mService.isCompanionApplicationBound( mContext.getOpPackageName(), mContext.getUserId()); @@ -1513,6 +1663,11 @@ public final class CompanionDeviceManager { @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) public void enableSecureTransport(boolean enabled) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + try { mService.enableSecureTransport(enabled); } catch (RemoteException e) { @@ -1534,6 +1689,11 @@ public final class CompanionDeviceManager { @FlaggedApi(Flags.FLAG_ASSOCIATION_TAG) @UserHandleAware public void setAssociationTag(int associationId, @NonNull String tag) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + Objects.requireNonNull(tag, "tag cannot be null"); if (tag.length() > ASSOCIATION_TAG_LENGTH_LIMIT) { @@ -1560,6 +1720,11 @@ public final class CompanionDeviceManager { @FlaggedApi(Flags.FLAG_ASSOCIATION_TAG) @UserHandleAware public void clearAssociationTag(int associationId) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + try { mService.clearAssociationTag(associationId); } catch (RemoteException e) { @@ -1567,15 +1732,6 @@ public final class CompanionDeviceManager { } } - private boolean checkFeaturePresent() { - boolean featurePresent = mService != null; - if (!featurePresent && DEBUG) { - Log.d(LOG_TAG, "Feature " + PackageManager.FEATURE_COMPANION_DEVICE_SETUP - + " not available"); - } - return featurePresent; - } - private static class AssociationRequestCallbackProxy extends IAssociationRequestCallback.Stub { private final Handler mHandler; private final Callback mCallback; @@ -1613,7 +1769,7 @@ public final class CompanionDeviceManager { private <T> void execute(Consumer<T> callback, T arg) { if (mExecutor != null) { mExecutor.execute(() -> callback.accept(arg)); - } else { + } else if (mHandler != null) { mHandler.post(() -> callback.accept(arg)); } } @@ -1716,6 +1872,11 @@ public final class CompanionDeviceManager { } public void start() throws IOException { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + final ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair(); final ParcelFileDescriptor localFd = pair[0]; final ParcelFileDescriptor remoteFd = pair[1]; @@ -1734,7 +1895,7 @@ public final class CompanionDeviceManager { copyWithFlushing(mLocalIn, mRemoteOut); } catch (IOException e) { if (!mStopped) { - Log.w(LOG_TAG, "Trouble during outgoing transport", e); + Log.w(TAG, "Trouble during outgoing transport", e); stop(); } } @@ -1744,7 +1905,7 @@ public final class CompanionDeviceManager { copyWithFlushing(mRemoteIn, mLocalOut); } catch (IOException e) { if (!mStopped) { - Log.w(LOG_TAG, "Trouble during incoming transport", e); + Log.w(TAG, "Trouble during incoming transport", e); stop(); } } @@ -1752,13 +1913,18 @@ public final class CompanionDeviceManager { } public void stop() { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return; + } + mStopped = true; try { mService.detachSystemDataTransport(mContext.getPackageName(), mContext.getUserId(), mAssociationId); } catch (RemoteException e) { - Log.w(LOG_TAG, "Failed to detach transport", e); + Log.w(TAG, "Failed to detach transport", e); } IoUtils.closeQuietly(mRemoteIn); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index bff90f1d2298..5d4babb8a36d 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -847,5 +847,5 @@ interface IPackageManager { @EnforcePermission("GET_APP_METADATA") int getAppMetadataSource(String packageName, int userId); - ComponentName getDomainVerificationAgent(); + ComponentName getDomainVerificationAgent(int userId); } diff --git a/core/java/android/hardware/face/FaceSensorConfigurations.java b/core/java/android/hardware/face/FaceSensorConfigurations.java index 6ef692f81069..12471681f913 100644 --- a/core/java/android/hardware/face/FaceSensorConfigurations.java +++ b/core/java/android/hardware/face/FaceSensorConfigurations.java @@ -22,11 +22,12 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.SensorProps; +import android.os.Binder; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; -import android.util.Pair; import android.util.Slog; import androidx.annotation.NonNull; @@ -36,7 +37,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.Function; /** * Provides the sensor props for face sensor, if available. @@ -74,22 +74,10 @@ public class FaceSensorConfigurations implements Parcelable { /** * Process AIDL instances to extract sensor props and add it to the sensor map. * @param aidlInstances available face AIDL instances - * @param getIFace function that provides the daemon for the specific instance */ - public void addAidlConfigs(@NonNull String[] aidlInstances, - @NonNull Function<String, IFace> getIFace) { + public void addAidlConfigs(@NonNull String[] aidlInstances) { for (String aidlInstance : aidlInstances) { - final String fqName = IFace.DESCRIPTOR + "/" + aidlInstance; - IFace face = getIFace.apply(fqName); - try { - if (face != null) { - mSensorPropsMap.put(aidlInstance, face.getSensorProps()); - } else { - Slog.e(TAG, "Unable to get declared service: " + fqName); - } - } catch (RemoteException e) { - Log.d(TAG, "Unable to get sensor properties!"); - } + mSensorPropsMap.put(aidlInstance, null); } } @@ -131,38 +119,31 @@ public class FaceSensorConfigurations implements Parcelable { } /** - * Return sensor props for the given instance. If instance is not available, - * then null is returned. + * Checks if {@param instance} exists. */ @Nullable - public Pair<String, SensorProps[]> getSensorPairForInstance(String instance) { - if (mSensorPropsMap.containsKey(instance)) { - return new Pair<>(instance, mSensorPropsMap.get(instance)); - } - - return null; + public boolean doesInstanceExist(String instance) { + return mSensorPropsMap.containsKey(instance); } /** - * Return the first pair of instance and sensor props, which does not correspond to the given - * If instance is not available, then null is returned. + * Return the first HAL instance, which does not correspond to the given {@param instance}. + * If another instance is not available, then null is returned. */ @Nullable - public Pair<String, SensorProps[]> getSensorPairNotForInstance(String instance) { + public String getSensorNameNotForInstance(String instance) { Optional<String> notAVirtualInstance = mSensorPropsMap.keySet().stream().filter( (instanceName) -> !instanceName.equals(instance)).findFirst(); - return notAVirtualInstance.map(this::getSensorPairForInstance).orElseGet( - this::getSensorPair); + return notAVirtualInstance.orElse(null); } /** - * Returns the first pair of instance and sensor props that has been added to the map. + * Returns the first instance that has been added to the map. */ @Nullable - public Pair<String, SensorProps[]> getSensorPair() { + public String getSensorInstance() { Optional<String> optionalInstance = mSensorPropsMap.keySet().stream().findFirst(); - return optionalInstance.map(this::getSensorPairForInstance).orElse(null); - + return optionalInstance.orElse(null); } public boolean getResetLockoutRequiresChallenge() { @@ -179,4 +160,31 @@ public class FaceSensorConfigurations implements Parcelable { dest.writeByte((byte) (mResetLockoutRequiresChallenge ? 1 : 0)); dest.writeMap(mSensorPropsMap); } + + /** + * Returns face sensor props for the HAL {@param instance}. + */ + @Nullable + public SensorProps[] getSensorPropForInstance(String instance) { + SensorProps[] props = mSensorPropsMap.get(instance); + + //Props should not be null for HIDL configs + if (props != null) { + return props; + } + + final String fqName = IFace.DESCRIPTOR + "/" + instance; + IFace face = IFace.Stub.asInterface(Binder.allowBlocking( + ServiceManager.waitForDeclaredService(fqName))); + try { + if (face != null) { + props = face.getSensorProps(); + } else { + Slog.e(TAG, "Unable to get declared service: " + fqName); + } + } catch (RemoteException e) { + Log.d(TAG, "Unable to get sensor properties!"); + } + return props; + } } diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java index f214494a5d7b..43c0da9bd8ed 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java @@ -23,18 +23,18 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorProps; +import android.os.Binder; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; -import android.util.Pair; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.Function; /** * Provides the sensor props for fingerprint sensor, if available. @@ -68,23 +68,10 @@ public class FingerprintSensorConfigurations implements Parcelable { /** * Process AIDL instances to extract sensor props and add it to the sensor map. * @param aidlInstances available face AIDL instances - * @param getIFingerprint function that provides the daemon for the specific instance */ - public void addAidlSensors(@NonNull String[] aidlInstances, - @NonNull Function<String, IFingerprint> getIFingerprint) { + public void addAidlSensors(@NonNull String[] aidlInstances) { for (String aidlInstance : aidlInstances) { - try { - final String fqName = IFingerprint.DESCRIPTOR + "/" + aidlInstance; - final IFingerprint fp = getIFingerprint.apply(fqName); - if (fp != null) { - SensorProps[] props = fp.getSensorProps(); - mSensorPropsMap.put(aidlInstance, props); - } else { - Log.d(TAG, "IFingerprint null for instance " + aidlInstance); - } - } catch (RemoteException e) { - Log.d(TAG, "Unable to get sensor properties!"); - } + mSensorPropsMap.put(aidlInstance, null); } } @@ -133,38 +120,31 @@ public class FingerprintSensorConfigurations implements Parcelable { } /** - * Return sensor props for the given instance. If instance is not available, - * then null is returned. + * Checks if {@param instance} exists. */ @Nullable - public Pair<String, SensorProps[]> getSensorPairForInstance(String instance) { - if (mSensorPropsMap.containsKey(instance)) { - return new Pair<>(instance, mSensorPropsMap.get(instance)); - } - - return null; + public boolean doesInstanceExist(String instance) { + return mSensorPropsMap.containsKey(instance); } /** - * Return the first pair of instance and sensor props, which does not correspond to the given - * If instance is not available, then null is returned. + * Return the first HAL instance, which does not correspond to the given {@param instance}. + * If another instance is not available, then null is returned. */ @Nullable - public Pair<String, SensorProps[]> getSensorPairNotForInstance(String instance) { + public String getSensorNameNotForInstance(String instance) { Optional<String> notAVirtualInstance = mSensorPropsMap.keySet().stream().filter( (instanceName) -> !instanceName.equals(instance)).findFirst(); - return notAVirtualInstance.map(this::getSensorPairForInstance).orElseGet( - this::getSensorPair); + return notAVirtualInstance.orElse(null); } /** - * Returns the first pair of instance and sensor props that has been added to the map. + * Returns the first instance that has been added to the map. */ @Nullable - public Pair<String, SensorProps[]> getSensorPair() { + public String getSensorInstance() { Optional<String> optionalInstance = mSensorPropsMap.keySet().stream().findFirst(); - return optionalInstance.map(this::getSensorPairForInstance).orElse(null); - + return optionalInstance.orElse(null); } public boolean getResetLockoutRequiresHardwareAuthToken() { @@ -181,4 +161,31 @@ public class FingerprintSensorConfigurations implements Parcelable { dest.writeByte((byte) (mResetLockoutRequiresHardwareAuthToken ? 1 : 0)); dest.writeMap(mSensorPropsMap); } + + /** + * Returns fingerprint sensor props for the HAL {@param instance}. + */ + @Nullable + public SensorProps[] getSensorPropForInstance(String instance) { + SensorProps[] props = mSensorPropsMap.get(instance); + + //Props should not be null for HIDL configs + if (props != null) { + return props; + } + + try { + final String fqName = IFingerprint.DESCRIPTOR + "/" + instance; + final IFingerprint fp = IFingerprint.Stub.asInterface(Binder.allowBlocking( + ServiceManager.waitForDeclaredService(fqName))); + if (fp != null) { + props = fp.getSensorProps(); + } else { + Log.d(TAG, "IFingerprint null for instance " + instance); + } + } catch (RemoteException e) { + Log.d(TAG, "Unable to get sensor properties!"); + } + return props; + } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 363e252b8f27..d91b051c671a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12451,7 +12451,7 @@ public final class Settings { /** @hide */ public static final int PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY = 1; /** @hide */ - public static final int PRIVATE_SPACE_AUTO_LOCK_NEVER = 2; + public static final int PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART = 2; /** * The different auto lock options for private space. @@ -12461,7 +12461,7 @@ public final class Settings { @IntDef(prefix = {"PRIVATE_SPACE_AUTO_LOCK_"}, value = { PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK, PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY, - PRIVATE_SPACE_AUTO_LOCK_NEVER, + PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART, }) @Retention(RetentionPolicy.SOURCE) public @interface PrivateSpaceAutoLockOption { diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java index e6d8fd0e2f2b..8a3f6ceb852b 100644 --- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java +++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java @@ -71,9 +71,11 @@ class QuickAccessWalletServiceInfo { @Nullable static QuickAccessWalletServiceInfo tryCreate(@NonNull Context context) { - String defaultAppPackageName = getDefaultWalletApp(context); + String defaultAppPackageName = null; - if (defaultAppPackageName == null) { + if (isWalletRoleAvailable(context)) { + defaultAppPackageName = getDefaultWalletApp(context); + } else { ComponentName defaultPaymentApp = getDefaultPaymentApp(context); if (defaultPaymentApp == null) { return null; @@ -103,14 +105,21 @@ class QuickAccessWalletServiceInfo { final long token = Binder.clearCallingIdentity(); try { RoleManager roleManager = context.getSystemService(RoleManager.class); - if (roleManager.isRoleAvailable(RoleManager.ROLE_WALLET)) { - List<String> roleHolders = roleManager.getRoleHolders(RoleManager.ROLE_WALLET); - return roleHolders.isEmpty() ? null : roleHolders.get(0); - } + List<String> roleHolders = roleManager.getRoleHolders(RoleManager.ROLE_WALLET); + return roleHolders.isEmpty() ? null : roleHolders.get(0); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private static boolean isWalletRoleAvailable(Context context) { + final long token = Binder.clearCallingIdentity(); + try { + RoleManager roleManager = context.getSystemService(RoleManager.class); + return roleManager.isRoleAvailable(RoleManager.ROLE_WALLET); } finally { Binder.restoreCallingIdentity(token); } - return null; } private static ComponentName getDefaultPaymentApp(Context context) { diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java index ca2e56d383e5..2e39f73f6233 100644 --- a/core/java/android/view/BatchedInputEventReceiver.java +++ b/core/java/android/view/BatchedInputEventReceiver.java @@ -29,6 +29,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver { private Choreographer mChoreographer; private boolean mBatchingEnabled; private boolean mBatchedInputScheduled; + private final String mTag; private final Handler mHandler; private final Runnable mConsumeBatchedInputEvents = new Runnable() { @Override @@ -43,6 +44,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver { super(inputChannel, looper); mChoreographer = choreographer; mBatchingEnabled = true; + mTag = inputChannel.getName(); traceBoolVariable("mBatchingEnabled", mBatchingEnabled); traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled); mHandler = new Handler(looper); @@ -123,7 +125,12 @@ public class BatchedInputEventReceiver extends InputEventReceiver { private final class BatchedInputRunnable implements Runnable { @Override public void run() { - doConsumeBatchedInput(mChoreographer.getFrameTimeNanos()); + try { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, mTag); + doConsumeBatchedInput(mChoreographer.getFrameTimeNanos()); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } } } private final BatchedInputRunnable mBatchedInputRunnable = new BatchedInputRunnable(); diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 147d56253dcf..17d14042e468 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -16,8 +16,10 @@ package android.view; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.annotation.XmlRes; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -39,6 +41,7 @@ import android.os.Parcelable; import android.os.PointerIconType; import android.util.Log; import android.util.SparseArray; +import android.view.flags.Flags; import com.android.internal.util.XmlUtils; @@ -637,4 +640,15 @@ public final class PointerIcon implements Parcelable { default: return Integer.toString(type); } } + + /** + * Sets whether drop shadow will draw in the native code. + * + * @hide + */ + @TestApi + @FlaggedApi(Flags.FLAG_ENABLE_VECTOR_CURSORS) + public void setDrawNativeDropShadow(boolean drawNativeDropShadow) { + mDrawNativeDropShadow = drawNativeDropShadow; + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 49d4af8a3e61..0d9e471e77af 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -10173,13 +10173,18 @@ public final class ViewRootImpl implements ViewParent, final class ConsumeBatchedInputRunnable implements Runnable { @Override public void run() { - mConsumeBatchedInputScheduled = false; - if (doConsumeBatchedInput(mChoreographer.getFrameTimeNanos())) { - // If we consumed a batch here, we want to go ahead and schedule the - // consumption of batched input events on the next frame. Otherwise, we would - // wait until we have more input events pending and might get starved by other - // things occurring in the process. - scheduleConsumeBatchedInput(); + Trace.traceBegin(TRACE_TAG_VIEW, mTag); + try { + mConsumeBatchedInputScheduled = false; + if (doConsumeBatchedInput(mChoreographer.getFrameTimeNanos())) { + // If we consumed a batch here, we want to go ahead and schedule the + // consumption of batched input events on the next frame. Otherwise, we would + // wait until we have more input events pending and might get starved by other + // things occurring in the process. + scheduleConsumeBatchedInput(); + } + } finally { + Trace.traceEnd(TRACE_TAG_VIEW); } } } diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java index c14d8d805d29..8063be64f36d 100644 --- a/core/java/com/android/internal/os/BinderInternal.java +++ b/core/java/com/android/internal/os/BinderInternal.java @@ -45,8 +45,8 @@ public class BinderInternal { static ArrayList<Runnable> sGcWatchers = new ArrayList<>(); static Runnable[] sTmpWatchers = new Runnable[1]; static long sLastGcTime; - static final BinderProxyLimitListenerDelegate sBinderProxyLimitListenerDelegate = - new BinderProxyLimitListenerDelegate(); + static final BinderProxyCountEventListenerDelegate sBinderProxyCountEventListenerDelegate = + new BinderProxyCountEventListenerDelegate(); static final class GcWatcher { @Override @@ -226,15 +226,24 @@ public class BinderInternal { * @param low The threshold a binder count must drop below before the callback * can be called again. (This is to avoid many repeated calls to the * callback in a brief period of time) + * @param warning The threshold between {@code high} and {@code low} where if the binder count + * exceeds that, the warning callback would be triggered. */ - public static final native void nSetBinderProxyCountWatermarks(int high, int low); + public static final native void nSetBinderProxyCountWatermarks(int high, int low, int warning); /** * Interface for callback invocation when the Binder Proxy limit is reached. onLimitReached will * be called with the uid of the app causing too many Binder Proxies */ - public interface BinderProxyLimitListener { + public interface BinderProxyCountEventListener { public void onLimitReached(int uid); + + /** + * Call when the number of binder proxies from the uid of the app reaches + * the warning threshold. + */ + default void onWarningThresholdReached(int uid) { + } } /** @@ -243,7 +252,17 @@ public class BinderInternal { * @param uid The uid of the bad behaving app sending too many binders */ public static void binderProxyLimitCallbackFromNative(int uid) { - sBinderProxyLimitListenerDelegate.notifyClient(uid); + sBinderProxyCountEventListenerDelegate.notifyLimitReached(uid); + } + + /** + * Callback used by native code to trigger a callback in java code. The callback will be + * triggered when too many binder proxies from a uid hits the warning limit. + * @param uid The uid of the bad behaving app sending too many binders + */ + @SuppressWarnings("unused") + public static void binderProxyWarningCallbackFromNative(int uid) { + sBinderProxyCountEventListenerDelegate.notifyWarningReached(uid); } /** @@ -252,41 +271,45 @@ public class BinderInternal { * @param handler must not be null, callback will be posted through the handler; * */ - public static void setBinderProxyCountCallback(BinderProxyLimitListener listener, + public static void setBinderProxyCountCallback(BinderProxyCountEventListener listener, @NonNull Handler handler) { Preconditions.checkNotNull(handler, "Must provide NonNull Handler to setBinderProxyCountCallback when setting " - + "BinderProxyLimitListener"); - sBinderProxyLimitListenerDelegate.setListener(listener, handler); + + "BinderProxyCountEventListener"); + sBinderProxyCountEventListenerDelegate.setListener(listener, handler); } /** * Clear the Binder Proxy callback */ public static void clearBinderProxyCountCallback() { - sBinderProxyLimitListenerDelegate.setListener(null, null); + sBinderProxyCountEventListenerDelegate.setListener(null, null); } - static private class BinderProxyLimitListenerDelegate { - private BinderProxyLimitListener mBinderProxyLimitListener; + private static class BinderProxyCountEventListenerDelegate { + private BinderProxyCountEventListener mBinderProxyCountEventListener; private Handler mHandler; - void setListener(BinderProxyLimitListener listener, Handler handler) { + void setListener(BinderProxyCountEventListener listener, Handler handler) { synchronized (this) { - mBinderProxyLimitListener = listener; + mBinderProxyCountEventListener = listener; mHandler = handler; } } - void notifyClient(final int uid) { + void notifyLimitReached(final int uid) { + synchronized (this) { + if (mBinderProxyCountEventListener != null) { + mHandler.post(() -> mBinderProxyCountEventListener.onLimitReached(uid)); + } + } + } + + void notifyWarningReached(final int uid) { synchronized (this) { - if (mBinderProxyLimitListener != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - mBinderProxyLimitListener.onLimitReached(uid); - } - }); + if (mBinderProxyCountEventListener != null) { + mHandler.post(() -> + mBinderProxyCountEventListener.onWarningThresholdReached(uid)); } } } diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java index 9df93f9a50f7..e12becd87e12 100644 --- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java @@ -407,8 +407,10 @@ public class ParsingPackageUtils { */ private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile, int flags) { + // The signature parsing will be done later in method parseBaseApk. + int liteParseFlags = flags & ~PARSE_COLLECT_CERTIFICATES; final ParseResult<PackageLite> liteResult = - ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags); + ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, liteParseFlags); if (liteResult.isError()) { return input.error(liteResult); } diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index d2d5186eb8a1..2068bd7bc8ea 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -88,6 +88,7 @@ static struct binderinternal_offsets_t jclass mClass; jmethodID mForceGc; jmethodID mProxyLimitCallback; + jmethodID mProxyWarningCallback; } gBinderInternalOffsets; @@ -1240,7 +1241,7 @@ static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz) gCollectedAtRefs = gNumLocalRefsCreated + gNumDeathRefsCreated; } -static void android_os_BinderInternal_proxyLimitcallback(int uid) +static void android_os_BinderInternal_proxyLimitCallback(int uid) { JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallStaticVoidMethod(gBinderInternalOffsets.mClass, @@ -1254,6 +1255,20 @@ static void android_os_BinderInternal_proxyLimitcallback(int uid) } } +static void android_os_BinderInternal_proxyWarningCallback(int uid) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->CallStaticVoidMethod(gBinderInternalOffsets.mClass, + gBinderInternalOffsets.mProxyWarningCallback, + uid); + + if (env->ExceptionCheck()) { + ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred()); + binder_report_exception(env, excep.get(), + "*** Uncaught exception in binderProxyWarningCallbackFromNative"); + } +} + static void android_os_BinderInternal_setBinderProxyCountEnabled(JNIEnv* env, jobject clazz, jboolean enable) { @@ -1278,9 +1293,10 @@ static jint android_os_BinderInternal_getBinderProxyCount(JNIEnv* env, jobject c } static void android_os_BinderInternal_setBinderProxyCountWatermarks(JNIEnv* env, jobject clazz, - jint high, jint low) + jint high, jint low, + jint warning) { - BpBinder::setBinderProxyCountWatermarks(high, low); + BpBinder::setBinderProxyCountWatermarks(high, low, warning); } // ---------------------------------------------------------------------------- @@ -1295,7 +1311,7 @@ static const JNINativeMethod gBinderInternalMethods[] = { { "nSetBinderProxyCountEnabled", "(Z)V", (void*)android_os_BinderInternal_setBinderProxyCountEnabled }, { "nGetBinderProxyPerUidCounts", "()Landroid/util/SparseIntArray;", (void*)android_os_BinderInternal_getBinderProxyPerUidCounts }, { "nGetBinderProxyCount", "(I)I", (void*)android_os_BinderInternal_getBinderProxyCount }, - { "nSetBinderProxyCountWatermarks", "(II)V", (void*)android_os_BinderInternal_setBinderProxyCountWatermarks} + { "nSetBinderProxyCountWatermarks", "(III)V", (void*)android_os_BinderInternal_setBinderProxyCountWatermarks} }; const char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal"; @@ -1307,6 +1323,8 @@ static int int_register_android_os_BinderInternal(JNIEnv* env) gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz); gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, "forceBinderGc", "()V"); gBinderInternalOffsets.mProxyLimitCallback = GetStaticMethodIDOrDie(env, clazz, "binderProxyLimitCallbackFromNative", "(I)V"); + gBinderInternalOffsets.mProxyWarningCallback = + GetStaticMethodIDOrDie(env, clazz, "binderProxyWarningCallbackFromNative", "(I)V"); jclass SparseIntArrayClass = FindClassOrDie(env, "android/util/SparseIntArray"); gSparseIntArrayOffsets.classObject = MakeGlobalRefOrDie(env, SparseIntArrayClass); @@ -1315,7 +1333,8 @@ static int int_register_android_os_BinderInternal(JNIEnv* env) gSparseIntArrayOffsets.put = GetMethodIDOrDie(env, gSparseIntArrayOffsets.classObject, "put", "(II)V"); - BpBinder::setLimitCallback(android_os_BinderInternal_proxyLimitcallback); + BpBinder::setBinderProxyCountEventCallback(android_os_BinderInternal_proxyLimitCallback, + android_os_BinderInternal_proxyWarningCallback); return RegisterMethodsOrDie( env, kBinderInternalPathName, diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 24031cad0a3e..48082048691c 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -134,6 +134,8 @@ android_test { ":BinderDeathRecipientHelperApp1", ":BinderDeathRecipientHelperApp2", ":com.android.cts.helpers.aosp", + ":BinderProxyCountingTestApp", + ":BinderProxyCountingTestService", ], } diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml index 05b309b2cd52..bf2a5b875dba 100644 --- a/core/tests/coretests/AndroidTest.xml +++ b/core/tests/coretests/AndroidTest.xml @@ -22,6 +22,8 @@ <option name="test-file-name" value="FrameworksCoreTests.apk" /> <option name="test-file-name" value="BinderDeathRecipientHelperApp1.apk" /> <option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" /> + <option name="test-file-name" value="BinderProxyCountingTestApp.apk" /> + <option name="test-file-name" value="BinderProxyCountingTestService.apk" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> diff --git a/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml b/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml index a971730f389d..c8407b80cfac 100644 --- a/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml +++ b/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml @@ -16,6 +16,9 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.coretests.binderproxycountingtestapp"> + <queries> + <package android:name="com.android.frameworks.coretests.binderproxycountingtestservice" /> + </queries> <application> <service android:name=".BpcTestAppCmdService" android:exported="true"/> diff --git a/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java b/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java index 5aae1203e559..a7e97d3cd54e 100644 --- a/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java +++ b/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java @@ -17,14 +17,15 @@ package com.android.frameworks.coretests.binderproxycountingtestapp; import android.app.Service; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; +import android.database.ContentObserver; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.provider.Settings; import android.util.Log; import com.android.frameworks.coretests.aidl.IBinderProxyCountingService; @@ -49,24 +50,20 @@ public class BpcTestAppCmdService extends Service { private IBpcTestAppCmdService.Stub mBinder = new IBpcTestAppCmdService.Stub() { - private ArrayList<BroadcastReceiver> mBrList = new ArrayList(); + private ArrayList<ContentObserver> mCoList = new ArrayList(); private ArrayList<ITestRemoteCallback> mTrcList = new ArrayList(); + private Handler mHandler = new Handler(); @Override public void createSystemBinders(int count) { int i = 0; while (i++ < count) { - BroadcastReceiver br = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - - } - }; - IntentFilter filt = new IntentFilter(Intent.ACTION_POWER_DISCONNECTED); - synchronized (mBrList) { - mBrList.add(br); + final ContentObserver co = new ContentObserver(mHandler) {}; + synchronized (mCoList) { + mCoList.add(co); } - registerReceiver(br, filt); + getContentResolver().registerContentObserver( + Settings.System.CONTENT_URI, false, co); } } @@ -74,11 +71,11 @@ public class BpcTestAppCmdService extends Service { public void releaseSystemBinders(int count) { int i = 0; while (i++ < count) { - BroadcastReceiver br; - synchronized (mBrList) { - br = mBrList.remove(0); + ContentObserver co; + synchronized (mCoList) { + co = mCoList.remove(0); } - unregisterReceiver(br); + getContentResolver().unregisterContentObserver(co); } } @@ -117,9 +114,9 @@ public class BpcTestAppCmdService extends Service { @Override public void releaseAllBinders() { - synchronized (mBrList) { - while (mBrList.size() > 0) { - unregisterReceiver(mBrList.remove(0)); + synchronized (mCoList) { + while (mCoList.size() > 0) { + getContentResolver().unregisterContentObserver(mCoList.remove(0)); } } synchronized (mTrcList) { @@ -179,4 +176,4 @@ public class BpcTestAppCmdService extends Service { public IBinder onBind(Intent intent) { return mBinder; } -}
\ No newline at end of file +} diff --git a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java index 6bed2a2ec53f..0f1accc4a7e9 100644 --- a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java +++ b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java @@ -55,8 +55,8 @@ public class BpcTestServiceCmdService extends Service { } @Override - public void setBinderProxyWatermarks(int high, int low) { - BinderInternal.nSetBinderProxyCountWatermarks(high, low); + public void setBinderProxyWatermarks(int high, int low, int warning) { + BinderInternal.nSetBinderProxyCountWatermarks(high, low, warning); } @Override @@ -68,12 +68,23 @@ public class BpcTestServiceCmdService extends Service { public void setBinderProxyCountCallback(IBpcCallbackObserver observer) { if (observer != null) { BinderInternal.setBinderProxyCountCallback( - new BinderInternal.BinderProxyLimitListener() { + new BinderInternal.BinderProxyCountEventListener() { @Override public void onLimitReached(int uid) { try { synchronized (observer) { - observer.onCallback(uid); + observer.onLimitReached(uid); + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + } + } + + @Override + public void onWarningThresholdReached(int uid) { + try { + synchronized (observer) { + observer.onWarningThresholdReached(uid); } } catch (Exception e) { Log.e(TAG, e.toString()); @@ -98,4 +109,4 @@ public class BpcTestServiceCmdService extends Service { mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); } -}
\ No newline at end of file +} diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl index c4ebd56bed6c..ada7d92c89e1 100644 --- a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl +++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl @@ -17,5 +17,6 @@ package com.android.frameworks.coretests.aidl; interface IBpcCallbackObserver { - void onCallback(int uid); -}
\ No newline at end of file + void onLimitReached(int uid); + void onWarningThresholdReached(int uid); +} diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl index abdab41ce537..cdcda9d93bd2 100644 --- a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl +++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl @@ -20,7 +20,7 @@ import com.android.frameworks.coretests.aidl.IBpcCallbackObserver; interface IBpcTestServiceCmdService { void forceGc(); int getBinderProxyCount(int uid); - void setBinderProxyWatermarks(int high, int low); + void setBinderProxyWatermarks(int high, int low, int warning); void enableBinderProxyLimit(boolean enable); void setBinderProxyCountCallback(IBpcCallbackObserver observer); -}
\ No newline at end of file +} diff --git a/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java b/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java index b61104d6f389..7d7f8ed25869 100644 --- a/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java +++ b/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java @@ -18,13 +18,10 @@ package android.hardware.face; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; -import android.hardware.biometrics.face.IFace; -import android.hardware.biometrics.face.SensorProps; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -37,8 +34,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import java.util.function.Function; - @Presubmit @SmallTest public class FaceSensorConfigurationsTest { @@ -48,10 +43,6 @@ public class FaceSensorConfigurationsTest { private Context mContext; @Mock private Resources mResources; - @Mock - private IFace mFace; - @Mock - private Function<String, IFace> mGetIFace; private final String[] mAidlInstances = new String[]{"default", "virtual"}; private String[] mHidlConfigStrings = new String[]{"0:2:15", "0:8:15"}; @@ -59,15 +50,13 @@ public class FaceSensorConfigurationsTest { @Before public void setUp() throws RemoteException { - when(mGetIFace.apply(anyString())).thenReturn(mFace); - when(mFace.getSensorProps()).thenReturn(new SensorProps[]{}); when(mContext.getResources()).thenReturn(mResources); } @Test public void testAidlInstanceSensorProps() { mFaceSensorConfigurations = new FaceSensorConfigurations(false); - mFaceSensorConfigurations.addAidlConfigs(mAidlInstances, mGetIFace); + mFaceSensorConfigurations.addAidlConfigs(mAidlInstances); assertThat(mFaceSensorConfigurations.hasSensorConfigurations()).isTrue(); assertThat(!mFaceSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue(); diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java index f058c160373c..b2f8299a8a35 100644 --- a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java +++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java @@ -18,13 +18,10 @@ package android.hardware.fingerprint; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; -import android.hardware.biometrics.fingerprint.IFingerprint; -import android.hardware.biometrics.fingerprint.SensorProps; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -37,8 +34,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import java.util.function.Function; - @Presubmit @SmallTest public class FingerprintSensorConfigurationsTest { @@ -48,10 +43,6 @@ public class FingerprintSensorConfigurationsTest { private Context mContext; @Mock private Resources mResources; - @Mock - private IFingerprint mFingerprint; - @Mock - private Function<String, IFingerprint> mGetIFingerprint; private final String[] mAidlInstances = new String[]{"default", "virtual"}; private String[] mHidlConfigStrings = new String[]{"0:2:15", "0:8:15"}; @@ -59,8 +50,6 @@ public class FingerprintSensorConfigurationsTest { @Before public void setUp() throws RemoteException { - when(mGetIFingerprint.apply(anyString())).thenReturn(mFingerprint); - when(mFingerprint.getSensorProps()).thenReturn(new SensorProps[]{}); when(mContext.getResources()).thenReturn(mResources); } @@ -68,7 +57,7 @@ public class FingerprintSensorConfigurationsTest { public void testAidlInstanceSensorProps() { mFingerprintSensorConfigurations = new FingerprintSensorConfigurations( true /* resetLockoutRequiresHardwareAuthToken */); - mFingerprintSensorConfigurations.addAidlSensors(mAidlInstances, mGetIFingerprint); + mFingerprintSensorConfigurations.addAidlSensors(mAidlInstances); assertThat(mFingerprintSensorConfigurations.hasSensorConfigurations()).isTrue(); assertThat(!mFingerprintSensorConfigurations.isSingleSensorConfigurationPresent()) diff --git a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java index bcd9521019e4..84d299592f26 100644 --- a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java +++ b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java @@ -88,9 +88,10 @@ public class BinderProxyCountingTest { private static final int BIND_SERVICE_TIMEOUT_SEC = 5; private static final int TOO_MANY_BINDERS_TIMEOUT_SEC = 2; + private static final int TOO_MANY_BINDERS_WITH_KILL_TIMEOUT_SEC = 30; - // Keep in sync with sBinderProxyCountLimit in BpBinder.cpp - private static final int BINDER_PROXY_LIMIT = 2500; + // Keep in sync with BINDER_PROXY_HIGH_WATERMARK in ActivityManagerService.java + private static final int BINDER_PROXY_LIMIT = 6000; private static Context sContext; private static UiDevice sUiDevice; @@ -175,18 +176,26 @@ public class BinderProxyCountingTest { } } - private CountDownLatch createBinderLimitLatch() throws RemoteException { - final CountDownLatch latch = new CountDownLatch(1); + private CountDownLatch[] createBinderLimitLatch() throws RemoteException { + final CountDownLatch[] latches = new CountDownLatch[] { + new CountDownLatch(1), new CountDownLatch(1) + }; sBpcTestServiceCmdService.setBinderProxyCountCallback( new IBpcCallbackObserver.Stub() { @Override - public void onCallback(int uid) { + public void onLimitReached(int uid) { if (uid == sTestPkgUid) { - latch.countDown(); + latches[0].countDown(); + } + } + @Override + public void onWarningThresholdReached(int uid) { + if (uid == sTestPkgUid) { + latches[1].countDown(); } } }); - return latch; + return latches; } /** @@ -227,6 +236,7 @@ public class BinderProxyCountingTest { @Test public void testBinderProxyLimitBoundary() throws Exception { final int binderProxyLimit = 2000; + final int binderProxyWarning = 1900; final int rearmThreshold = 1800; try { sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent); @@ -238,19 +248,33 @@ public class BinderProxyCountingTest { // Get the baseline of binders naturally held by the test Package int baseBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid); - final CountDownLatch binderLimitLatch = createBinderLimitLatch(); - sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold); + final CountDownLatch[] binderLatches = createBinderLimitLatch(); + sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold, + binderProxyWarning); + + // Create Binder Proxies up to the warning; + sBpcTestAppCmdService.createTestBinders(binderProxyWarning - baseBinderCount); + if (binderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid + + " when proxy warning should not have been triggered"); + } + + // Create one more Binder to trigger the warning + sBpcTestAppCmdService.createTestBinders(1); + if (!binderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + fail("Timed out waiting for uid " + sTestPkgUid + " to trigger the warning"); + } // Create Binder Proxies up to the limit - sBpcTestAppCmdService.createTestBinders(binderProxyLimit - baseBinderCount); - if (binderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + sBpcTestAppCmdService.createTestBinders(binderProxyLimit - binderProxyWarning - 1); + if (binderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid + " when proxy limit should not have been reached"); } // Create one more Binder to cross the limit sBpcTestAppCmdService.createTestBinders(1); - if (!binderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + if (!binderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit"); } @@ -274,12 +298,20 @@ public class BinderProxyCountingTest { sBpcTestServiceCmdService.forceGc(); int baseBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid); for (int testLimit : testLimits) { - final CountDownLatch binderLimitLatch = createBinderLimitLatch(); + final CountDownLatch[] binderLatches = createBinderLimitLatch(); // Change the BinderProxyLimit - sBpcTestServiceCmdService.setBinderProxyWatermarks(testLimit, baseBinderCount + 10); + sBpcTestServiceCmdService.setBinderProxyWatermarks(testLimit, baseBinderCount + 10, + testLimit - 10); + + // Trigger the new Binder Proxy warning + sBpcTestAppCmdService.createTestBinders(testLimit - 9 - baseBinderCount); + if (!binderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + fail("Timed out waiting for uid " + sTestPkgUid + " to trigger the warning"); + } + // Exceed the new Binder Proxy Limit - sBpcTestAppCmdService.createTestBinders(testLimit + 1); - if (!binderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + sBpcTestAppCmdService.createTestBinders(10); + if (!binderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit"); } @@ -297,6 +329,7 @@ public class BinderProxyCountingTest { public void testRearmCallbackThreshold() throws Exception { final int binderProxyLimit = 2000; final int exceedBinderProxyLimit = binderProxyLimit + 10; + final int binderProxyWarning = 1900; final int rearmThreshold = 1800; try { sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent); @@ -305,11 +338,19 @@ public class BinderProxyCountingTest { sBpcTestServiceCmdService.enableBinderProxyLimit(true); sBpcTestServiceCmdService.forceGc(); - final CountDownLatch firstBinderLimitLatch = createBinderLimitLatch(); - sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold); + int baseBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid); + final CountDownLatch[] firstBinderLatches = createBinderLimitLatch(); + sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold, + binderProxyWarning); + // Trigger the Binder Proxy Waring + sBpcTestAppCmdService.createTestBinders(binderProxyWarning - baseBinderCount + 1); + if (!firstBinderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + fail("Timed out waiting for uid " + sTestPkgUid + " to trigger warning"); + } + // Exceed the Binder Proxy Limit - sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit); - if (!firstBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - binderProxyWarning); + if (!firstBinderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit"); } @@ -321,11 +362,20 @@ public class BinderProxyCountingTest { sBpcTestServiceCmdService.forceGc(); currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid); - final CountDownLatch secondBinderLimitLatch = createBinderLimitLatch(); + final CountDownLatch[] secondBinderLatches = createBinderLimitLatch(); + + // Exceed the Binder Proxy warning which should not cause a callback since there has + // been no rearm + sBpcTestAppCmdService.createTestBinders(binderProxyWarning - currentBinderCount + 1); + if (secondBinderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid + + " when the callback has not been rearmed yet"); + } + // Exceed the Binder Proxy limit which should not cause a callback since there has // been no rearm - sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - currentBinderCount); - if (secondBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - binderProxyWarning); + if (secondBinderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid + " when the callback has not been rearmed yet"); } @@ -337,10 +387,16 @@ public class BinderProxyCountingTest { sBpcTestServiceCmdService.forceGc(); currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid); + // Trigger the Binder Proxy Waring + sBpcTestAppCmdService.createTestBinders(binderProxyWarning - currentBinderCount + 1); + if (!secondBinderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + fail("Timed out waiting for uid " + sTestPkgUid + " to trigger warning"); + } + // Exceed the Binder Proxy limit for the last time sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - currentBinderCount); - if (!secondBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + if (!secondBinderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit"); } sBpcTestAppCmdService.releaseTestBinders(currentBinderCount); @@ -373,7 +429,7 @@ public class BinderProxyCountingTest { // is not unexpected } - if (!binderDeathLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) { + if (!binderDeathLatch.await(TOO_MANY_BINDERS_WITH_KILL_TIMEOUT_SEC, TimeUnit.SECONDS)) { sBpcTestAppCmdService.releaseSystemBinders(exceedBinderProxyLimit); fail("Timed out waiting for uid " + sTestPkgUid + " to die."); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index ad3be3d0b022..59c841f63498 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -686,7 +686,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } - private void dispatchOnBackCancelled(IOnBackInvokedCallback callback) { + private void tryDispatchOnBackCancelled(IOnBackInvokedCallback callback) { + if (!mOnBackStartDispatched) { + Log.e(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched."); + return; + } if (callback == null) { return; } @@ -745,7 +749,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (touchTracker.getTriggerBack()) { dispatchOrAnimateOnBackInvoked(callback, touchTracker); } else { - dispatchOnBackCancelled(callback); + tryDispatchOnBackCancelled(callback); } } finishBackNavigation(touchTracker.getTriggerBack()); @@ -824,7 +828,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (mCurrentTracker.getTriggerBack()) { dispatchOrAnimateOnBackInvoked(mActiveCallback, mCurrentTracker); } else { - dispatchOnBackCancelled(mActiveCallback); + tryDispatchOnBackCancelled(mActiveCallback); } } @@ -876,7 +880,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (mCurrentTracker.isInInitialState()) { if (mBackGestureStarted) { mBackGestureStarted = false; - dispatchOnBackCancelled(mActiveCallback); + tryDispatchOnBackCancelled(mActiveCallback); finishBackNavigation(false); ProtoLog.d(WM_SHELL_BACK_PREVIEW, "resetTouchTracker -> reset an unfinished gesture"); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index 2919782a758a..d839eae27171 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -557,6 +557,23 @@ public class BackAnimationControllerTest extends ShellTestCase { } @Test + public void skipsCancelWithoutStart() throws RemoteException { + final int type = BackNavigationInfo.TYPE_CALLBACK; + final ResultListener result = new ResultListener(); + createNavigationInfo(new BackNavigationInfo.Builder() + .setType(type) + .setOnBackInvokedCallback(mAppCallback) + .setOnBackNavigationDone(new RemoteCallback(result))); + doMotionEvent(MotionEvent.ACTION_CANCEL, 0); + mShellExecutor.flushAll(); + + verify(mAppCallback, never()).onBackStarted(any()); + verify(mAppCallback, never()).onBackProgressed(any()); + verify(mAppCallback, never()).onBackInvoked(); + verify(mAppCallback, never()).onBackCancelled(); + } + + @Test public void testBackToActivity() throws RemoteException { final CrossActivityBackAnimation animation = new CrossActivityBackAnimation( mContext, mAnimationBackground, mRootTaskDisplayAreaOrganizer); diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt index e7fec692bd63..65a5317ed0cb 100644 --- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt @@ -73,6 +73,10 @@ interface AudioRepository { suspend fun setVolume(audioStream: AudioStream, volume: Int) suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) + + suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) + + suspend fun isAffectedByMute(audioStream: AudioStream): Boolean } class AudioRepositoryImpl( @@ -169,6 +173,16 @@ class AudioRepositoryImpl( ) } + override suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) { + withContext(backgroundCoroutineContext) { audioManager.ringerMode = mode.value } + } + + override suspend fun isAffectedByMute(audioStream: AudioStream): Boolean { + return withContext(backgroundCoroutineContext) { + audioManager.isStreamAffectedByMute(audioStream.value) + } + } + private fun getMinVolume(stream: AudioStream): Int = try { audioManager.getStreamMinVolume(stream.value) diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt index 778653b9bd44..33f917e701c2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt +++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt @@ -49,8 +49,14 @@ class AudioVolumeInteractor( suspend fun setVolume(audioStream: AudioStream, volume: Int) = audioRepository.setVolume(audioStream, volume) - suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) = + suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) { + if (audioStream.value == AudioManager.STREAM_RING) { + val mode = + if (isMuted) AudioManager.RINGER_MODE_VIBRATE else AudioManager.RINGER_MODE_NORMAL + audioRepository.setRingerMode(audioStream, RingerMode(mode)) + } audioRepository.setMuted(audioStream, isMuted) + } /** Checks if the volume can be changed via the UI. */ fun canChangeVolume(audioStream: AudioStream): Flow<Boolean> { @@ -66,9 +72,8 @@ class AudioVolumeInteractor( } } - fun isMutable(audioStream: AudioStream): Boolean = - // Alarm stream doesn't support muting - audioStream.value != AudioManager.STREAM_ALARM + suspend fun isAffectedByMute(audioStream: AudioStream): Boolean = + audioRepository.isAffectedByMute(audioStream) private suspend fun processVolume( audioStreamModel: AudioStreamModel, diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt index dbdf970467b5..24cc8a4cfcec 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt @@ -711,7 +711,6 @@ private class AnimatedDialog( dialog.setDismissOverride(this::onDialogDismissed) if (featureFlags.isPredictiveBackQsDialogAnim) { - // TODO(b/265923095) Improve animations for QS dialogs on configuration change dialog.registerAnimationOnBackInvoked(targetView = dialogContentWithBackground) } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt index dd32851a97cf..4d327e1d8beb 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt @@ -37,7 +37,7 @@ fun interface BackAnimationSpec { /** Create a [BackAnimationSpec] from [displayMetrics] and design specs. */ fun BackAnimationSpec.Companion.createFloatingSurfaceAnimationSpec( - displayMetrics: DisplayMetrics, + displayMetricsProvider: () -> DisplayMetrics, maxMarginXdp: Float, maxMarginYdp: Float, minScale: Float, @@ -45,18 +45,19 @@ fun BackAnimationSpec.Companion.createFloatingSurfaceAnimationSpec( translateYEasing: Interpolator = Interpolators.LINEAR, scaleEasing: Interpolator = Interpolators.STANDARD_DECELERATE, ): BackAnimationSpec { - val screenWidthPx = displayMetrics.widthPixels - val screenHeightPx = displayMetrics.heightPixels + return BackAnimationSpec { backEvent, progressY, result -> + val displayMetrics = displayMetricsProvider() + val screenWidthPx = displayMetrics.widthPixels + val screenHeightPx = displayMetrics.heightPixels - val maxMarginXPx = maxMarginXdp.dpToPx(displayMetrics) - val maxMarginYPx = maxMarginYdp.dpToPx(displayMetrics) - val maxTranslationXByScale = (screenWidthPx - screenWidthPx * minScale) / 2 - val maxTranslationX = maxTranslationXByScale - maxMarginXPx - val maxTranslationYByScale = (screenHeightPx - screenHeightPx * minScale) / 2 - val maxTranslationY = maxTranslationYByScale - maxMarginYPx - val minScaleReversed = 1f - minScale + val maxMarginXPx = maxMarginXdp.dpToPx(displayMetrics) + val maxMarginYPx = maxMarginYdp.dpToPx(displayMetrics) + val maxTranslationXByScale = (screenWidthPx - screenWidthPx * minScale) / 2 + val maxTranslationX = maxTranslationXByScale - maxMarginXPx + val maxTranslationYByScale = (screenHeightPx - screenHeightPx * minScale) / 2 + val maxTranslationY = maxTranslationYByScale - maxMarginYPx + val minScaleReversed = 1f - minScale - return BackAnimationSpec { backEvent, progressY, result -> val direction = if (backEvent.swipeEdge == BackEvent.EDGE_LEFT) 1 else -1 val progressX = backEvent.progress diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt index b8d446961a88..b05729669f7c 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt @@ -23,10 +23,10 @@ import android.util.DisplayMetrics * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-1-dismiss-app */ fun BackAnimationSpec.Companion.dismissAppForSysUi( - displayMetrics: DisplayMetrics, + displayMetricsProvider: () -> DisplayMetrics, ): BackAnimationSpec = BackAnimationSpec.createFloatingSurfaceAnimationSpec( - displayMetrics = displayMetrics, + displayMetricsProvider = displayMetricsProvider, maxMarginXdp = 8f, maxMarginYdp = 8f, minScale = 0.8f, @@ -37,10 +37,10 @@ fun BackAnimationSpec.Companion.dismissAppForSysUi( * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-2-cross-task */ fun BackAnimationSpec.Companion.crossTaskForSysUi( - displayMetrics: DisplayMetrics, + displayMetricsProvider: () -> DisplayMetrics, ): BackAnimationSpec = BackAnimationSpec.createFloatingSurfaceAnimationSpec( - displayMetrics = displayMetrics, + displayMetricsProvider = displayMetricsProvider, maxMarginXdp = 8f, maxMarginYdp = 8f, minScale = 0.8f, @@ -51,10 +51,10 @@ fun BackAnimationSpec.Companion.crossTaskForSysUi( * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-3-inner-area-dismiss */ fun BackAnimationSpec.Companion.innerAreaDismissForSysUi( - displayMetrics: DisplayMetrics, + displayMetricsProvider: () -> DisplayMetrics, ): BackAnimationSpec = BackAnimationSpec.createFloatingSurfaceAnimationSpec( - displayMetrics = displayMetrics, + displayMetricsProvider = displayMetricsProvider, maxMarginXdp = 0f, maxMarginYdp = 0f, minScale = 0.9f, @@ -65,10 +65,10 @@ fun BackAnimationSpec.Companion.innerAreaDismissForSysUi( * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-4-floating-system-surfaces */ fun BackAnimationSpec.Companion.floatingSystemSurfacesForSysUi( - displayMetrics: DisplayMetrics, + displayMetricsProvider: () -> DisplayMetrics, ): BackAnimationSpec = BackAnimationSpec.createFloatingSurfaceAnimationSpec( - displayMetrics = displayMetrics, + displayMetricsProvider = displayMetricsProvider, maxMarginXdp = 8f, maxMarginYdp = 8f, minScale = 0.9f, diff --git a/packages/SystemUI/animation/src/com/android/systemui/util/Dialog.kt b/packages/SystemUI/animation/src/com/android/systemui/util/Dialog.kt index 0f63548b6f0c..9dd23289d8b5 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/util/Dialog.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/util/Dialog.kt @@ -39,7 +39,7 @@ fun Dialog.registerAnimationOnBackInvoked( targetView: View, backAnimationSpec: BackAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi( - displayMetrics = targetView.resources.displayMetrics, + displayMetricsProvider = { targetView.resources.displayMetrics }, ), ) { targetView.registerOnBackInvokedCallbackOnViewAttached( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index a1d8c29c2a39..bdd888f45182 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -8,12 +8,15 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.FixedSizeEdgeDetector import com.android.compose.animation.scene.LowestZIndexScenePicker +import com.android.compose.animation.scene.MutableSceneTransitionLayoutState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.SceneTransitionLayout @@ -21,12 +24,13 @@ import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.observableTransitionState import com.android.compose.animation.scene.transitions -import com.android.compose.animation.scene.updateSceneTransitionLayoutState import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.SceneDataSourceDelegator +import com.android.systemui.scene.ui.composable.SceneTransitionLayoutDataSource import com.android.systemui.statusbar.phone.SystemUIDialogFactory object Communal { @@ -63,27 +67,35 @@ val sceneTransitions = transitions { fun CommunalContainer( modifier: Modifier = Modifier, viewModel: CommunalViewModel, + dataSourceDelegator: SceneDataSourceDelegator, dialogFactory: SystemUIDialogFactory, ) { - val currentScene: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank) - val sceneTransitionLayoutState = - updateSceneTransitionLayoutState( - currentScene, - onChangeScene = { viewModel.onSceneChanged(it) }, + val coroutineScope = rememberCoroutineScope() + val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank) + val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false) + val state: MutableSceneTransitionLayoutState = remember { + MutableSceneTransitionLayoutState( + initialScene = currentSceneKey, transitions = sceneTransitions, enableInterruptions = false, ) - val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false) + } + + DisposableEffect(state) { + val dataSource = SceneTransitionLayoutDataSource(state, coroutineScope) + dataSourceDelegator.setDelegate(dataSource) + onDispose { dataSourceDelegator.setDelegate(null) } + } // This effect exposes the SceneTransitionLayout's observable transition state to the rest of // the system, and unsets it when the view is disposed to avoid a memory leak. - DisposableEffect(viewModel, sceneTransitionLayoutState) { - viewModel.setTransitionState(sceneTransitionLayoutState.observableTransitionState()) + DisposableEffect(viewModel, state) { + viewModel.setTransitionState(state.observableTransitionState()) onDispose { viewModel.setTransitionState(null) } } SceneTransitionLayout( - state = sceneTransitionLayoutState, + state = state, modifier = modifier.fillMaxSize(), swipeSourceDetector = FixedSizeEdgeDetector( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt index 1178cc843d60..4bef9efe79d1 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt @@ -16,7 +16,6 @@ package com.android.systemui.keyguard.ui.composable -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState @@ -24,9 +23,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView -import com.android.compose.animation.scene.SceneKey -import com.android.compose.animation.scene.SceneTransitionLayout -import com.android.compose.animation.scene.transitions +import com.android.compose.animation.scene.SceneScope import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel @@ -45,16 +42,12 @@ constructor( private val blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>, private val clockInteractor: KeyguardClockInteractor, ) { - - private val sceneKeyByBlueprint: Map<ComposableLockscreenSceneBlueprint, SceneKey> by lazy { - blueprints.associateWith { blueprint -> SceneKey(blueprint.id) } - } - private val sceneKeyByBlueprintId: Map<String, SceneKey> by lazy { - sceneKeyByBlueprint.entries.associate { (blueprint, sceneKey) -> blueprint.id to sceneKey } + private val blueprintByBlueprintId: Map<String, ComposableLockscreenSceneBlueprint> by lazy { + blueprints.associateBy { it.id } } @Composable - fun Content( + fun SceneScope.Content( modifier: Modifier = Modifier, ) { val coroutineScope = rememberCoroutineScope() @@ -66,19 +59,7 @@ constructor( onDispose { clockInteractor.clockEventController.unregisterListeners() } } - // Switch smoothly between blueprints, any composable tagged with element() will be - // transition-animated between any two blueprints, if they both display the same element. - SceneTransitionLayout( - currentScene = checkNotNull(sceneKeyByBlueprintId[blueprintId]), - onChangeScene = {}, - transitions = - transitions { sceneKeyByBlueprintId.values.forEach { sceneKey -> to(sceneKey) } }, - modifier = modifier, - enableInterruptions = false, - ) { - sceneKeyByBlueprint.entries.forEach { (blueprint, sceneKey) -> - scene(sceneKey) { with(blueprint) { Content(Modifier.fillMaxSize()) } } - } - } + val blueprint = blueprintByBlueprintId[blueprintId] ?: return + with(blueprint) { Content(modifier) } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt index 7acb4d5498db..c241f9ca7253 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt @@ -66,9 +66,5 @@ private fun SceneScope.LockscreenScene( key = QuickSettings.SharedValues.TilesSquishiness, ) - lockscreenContent - .get() - .Content( - modifier = modifier.fillMaxSize(), - ) + with(lockscreenContent.get()) { Content(modifier = modifier.fillMaxSize()) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt index 320c455a8201..c008a1a4d192 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt @@ -16,10 +16,15 @@ package com.android.systemui.keyguard.ui.composable.blueprint +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect @@ -28,6 +33,7 @@ import com.android.systemui.keyguard.ui.composable.LockscreenLongPress import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection import com.android.systemui.keyguard.ui.composable.section.LockSection +import com.android.systemui.keyguard.ui.composable.section.NotificationSection import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection import com.android.systemui.keyguard.ui.composable.section.StatusBarSection import com.android.systemui.keyguard.ui.composable.section.TopAreaSection @@ -52,6 +58,7 @@ constructor( private val bottomAreaSection: BottomAreaSection, private val settingsMenuSection: SettingsMenuSection, private val topAreaSection: TopAreaSection, + private val notificationSection: NotificationSection, ) : ComposableLockscreenSceneBlueprint { override val id: String = "default" @@ -59,6 +66,8 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { val isUdfpsVisible = viewModel.isUdfpsVisible + val shouldUseSplitNotificationShade by + viewModel.shouldUseSplitNotificationShade.collectAsState() LockscreenLongPress( viewModel = viewModel.longPress, @@ -68,10 +77,27 @@ constructor( content = { // Constrained to above the lock icon. Column( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxSize(), ) { with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) } - with(topAreaSection) { DefaultClockLayoutWithNotifications() } + + Box { + with(topAreaSection) { DefaultClockLayout() } + if (shouldUseSplitNotificationShade) { + with(notificationSection) { + Notifications( + Modifier.fillMaxWidth(0.5f) + .fillMaxHeight() + .align(alignment = Alignment.TopEnd) + ) + } + } + } + if (!shouldUseSplitNotificationShade) { + with(notificationSection) { + Notifications(Modifier.weight(weight = 1f)) + } + } if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) { with(ambientIndicationSectionOptional.get()) { AmbientIndication(modifier = Modifier.fillMaxWidth()) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt index 64c2cb3670c8..091a43923a6e 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt @@ -16,10 +16,15 @@ package com.android.systemui.keyguard.ui.composable.blueprint +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect @@ -28,6 +33,7 @@ import com.android.systemui.keyguard.ui.composable.LockscreenLongPress import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection import com.android.systemui.keyguard.ui.composable.section.LockSection +import com.android.systemui.keyguard.ui.composable.section.NotificationSection import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection import com.android.systemui.keyguard.ui.composable.section.StatusBarSection import com.android.systemui.keyguard.ui.composable.section.TopAreaSection @@ -52,6 +58,7 @@ constructor( private val bottomAreaSection: BottomAreaSection, private val settingsMenuSection: SettingsMenuSection, private val topAreaSection: TopAreaSection, + private val notificationSection: NotificationSection, ) : ComposableLockscreenSceneBlueprint { override val id: String = "shortcuts-besides-udfps" @@ -59,6 +66,8 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { val isUdfpsVisible = viewModel.isUdfpsVisible + val shouldUseSplitNotificationShade by + viewModel.shouldUseSplitNotificationShade.collectAsState() LockscreenLongPress( viewModel = viewModel.longPress, @@ -68,11 +77,27 @@ constructor( content = { // Constrained to above the lock icon. Column( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxSize(), ) { with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) } - with(topAreaSection) { DefaultClockLayoutWithNotifications() } + Box { + with(topAreaSection) { DefaultClockLayout() } + if (shouldUseSplitNotificationShade) { + with(notificationSection) { + Notifications( + Modifier.fillMaxWidth(0.5f) + .fillMaxHeight() + .align(alignment = Alignment.TopEnd) + ) + } + } + } + if (!shouldUseSplitNotificationShade) { + with(notificationSection) { + Notifications(Modifier.weight(weight = 1f)) + } + } if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) { with(ambientIndicationSectionOptional.get()) { AmbientIndication(modifier = Modifier.fillMaxWidth()) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt index fe774a0d6db2..09d76a341442 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt @@ -86,6 +86,7 @@ constructor( val burnIn = rememberBurnIn(clockInteractor) val resources = LocalContext.current.resources val currentClockState = clockViewModel.currentClock.collectAsState() + val areNotificationsVisible by viewModel.areNotificationsVisible.collectAsState() LockscreenLongPress( viewModel = viewModel.longPress, modifier = modifier, @@ -145,7 +146,7 @@ constructor( with(mediaCarouselSection) { MediaCarousel() } - if (viewModel.areNotificationsVisible) { + if (areNotificationsVisible) { with(notificationSection) { Notifications( modifier = Modifier.fillMaxWidth().weight(weight = 1f) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt index 6b86a484069b..fa0a1c4663b1 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt @@ -17,12 +17,25 @@ package com.android.systemui.keyguard.ui.composable.section import android.view.ViewGroup +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.SceneScope +import com.android.compose.modifiers.thenIf +import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.MigrateClocksToBlueprint +import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.notifications.ui.composable.NotificationStack +import com.android.systemui.res.R +import com.android.systemui.shade.LargeScreenHeaderHelper import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder @@ -39,6 +52,7 @@ constructor( sharedNotificationContainerViewModel: SharedNotificationContainerViewModel, stackScrollLayout: NotificationStackScrollLayout, sharedNotificationContainerBinder: SharedNotificationContainerBinder, + private val lockscreenContentViewModel: LockscreenContentViewModel, ) { init { @@ -65,9 +79,27 @@ constructor( @Composable fun SceneScope.Notifications(modifier: Modifier = Modifier) { + val shouldUseSplitNotificationShade by + lockscreenContentViewModel.shouldUseSplitNotificationShade.collectAsState() + val areNotificationsVisible by + lockscreenContentViewModel.areNotificationsVisible.collectAsState() + val splitShadeTopMargin: Dp = + if (Flags.centralizedStatusBarHeightFix()) { + LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp + } else { + dimensionResource(id = R.dimen.large_screen_shade_header_height) + } + + if (!areNotificationsVisible) { + return + } + NotificationStack( viewModel = viewModel, - modifier = modifier, + modifier = + modifier.fillMaxWidth().thenIf(shouldUseSplitNotificationShade) { + Modifier.padding(top = splitShadeTopMargin) + }, ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt index b4472fc15ac4..f8e63411ed52 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt @@ -16,30 +16,20 @@ package com.android.systemui.keyguard.ui.composable.section -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.dp +import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.SceneTransitionLayout import com.android.compose.modifiers.thenIf -import com.android.systemui.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene @@ -48,8 +38,6 @@ import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitSh import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel -import com.android.systemui.res.R -import com.android.systemui.shade.LargeScreenHeaderHelper import javax.inject.Inject class TopAreaSection @@ -58,19 +46,16 @@ constructor( private val clockViewModel: KeyguardClockViewModel, private val smartSpaceSection: SmartSpaceSection, private val mediaCarouselSection: MediaCarouselSection, - private val notificationSection: NotificationSection, private val clockSection: DefaultClockSection, private val clockInteractor: KeyguardClockInteractor, ) { @Composable - fun DefaultClockLayoutWithNotifications( + fun DefaultClockLayout( modifier: Modifier = Modifier, ) { - val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState() val currentClockLayout by clockViewModel.currentClockLayout.collectAsState() val hasCustomPositionUpdatedAnimation by clockViewModel.hasCustomPositionUpdatedAnimation.collectAsState() - val currentScene = when (currentClockLayout) { KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK -> @@ -81,144 +66,83 @@ constructor( KeyguardClockViewModel.ClockLayout.SMALL_CLOCK -> smallClockScene } - val splitShadeTopMargin: Dp = - if (Flags.centralizedStatusBarHeightFix()) { - LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp - } else { - dimensionResource(id = R.dimen.large_screen_shade_header_height) - } - val burnIn = rememberBurnIn(clockInteractor) - - LaunchedEffect(isLargeClockVisible) { - if (isLargeClockVisible) { - burnIn.onSmallClockTopChanged(null) - } - } - SceneTransitionLayout( - modifier = modifier.fillMaxSize(), + modifier = modifier, currentScene = currentScene, onChangeScene = {}, transitions = ClockTransition.defaultClockTransitions, enableInterruptions = false, ) { scene(splitShadeLargeClockScene) { - Box(modifier = Modifier.fillMaxSize()) { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - with(smartSpaceSection) { - SmartSpace( - burnInParams = burnIn.parameters, - onTopChanged = burnIn.onSmartspaceTopChanged, - ) - } - - with(clockSection) { - LargeClock( - modifier = - Modifier.fillMaxSize().thenIf( - !hasCustomPositionUpdatedAnimation - ) { - // If we do not have a custom position animation, we want - // the clock to be on one half of the screen. - Modifier.offset { - IntOffset( - x = - -clockSection - .getClockCenteringDistance() - .toInt(), - y = 0, - ) - } - } - ) - } - } - } - - Row( - modifier = Modifier.fillMaxSize(), - ) { - Spacer(modifier = Modifier.weight(weight = 1f)) - with(notificationSection) { - Notifications( - modifier = - Modifier.fillMaxHeight() - .weight(weight = 1f) - .padding(top = splitShadeTopMargin) - ) - } - } + LargeClockWithSmartSpace( + shouldOffSetClockToOneHalf = !hasCustomPositionUpdatedAnimation + ) } scene(splitShadeSmallClockScene) { - Row( - modifier = Modifier.fillMaxSize(), - ) { - Column( - modifier = Modifier.fillMaxHeight().weight(weight = 1f), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - with(clockSection) { - SmallClock( - burnInParams = burnIn.parameters, - onTopChanged = burnIn.onSmallClockTopChanged, - modifier = Modifier.wrapContentSize() - ) - } - with(smartSpaceSection) { - SmartSpace( - burnInParams = burnIn.parameters, - onTopChanged = burnIn.onSmartspaceTopChanged, - ) - } - with(mediaCarouselSection) { MediaCarousel() } - } - with(notificationSection) { - Notifications( - modifier = - Modifier.fillMaxHeight() - .weight(weight = 1f) - .padding(top = splitShadeTopMargin) - ) - } - } + SmallClockWithSmartSpace(modifier = Modifier.fillMaxWidth(0.5f)) } - scene(smallClockScene) { - Column { - with(clockSection) { - SmallClock( - burnInParams = burnIn.parameters, - onTopChanged = burnIn.onSmallClockTopChanged, - modifier = Modifier.wrapContentSize() - ) - } - with(smartSpaceSection) { - SmartSpace( - burnInParams = burnIn.parameters, - onTopChanged = burnIn.onSmartspaceTopChanged, - ) - } - with(mediaCarouselSection) { MediaCarousel() } - with(notificationSection) { - Notifications(modifier = Modifier.fillMaxWidth().weight(weight = 1f)) - } - } + scene(smallClockScene) { SmallClockWithSmartSpace() } + + scene(largeClockScene) { LargeClockWithSmartSpace() } + } + } + + @Composable + private fun SceneScope.SmallClockWithSmartSpace(modifier: Modifier = Modifier) { + val burnIn = rememberBurnIn(clockInteractor) + + Column(modifier = modifier) { + with(clockSection) { + SmallClock( + burnInParams = burnIn.parameters, + onTopChanged = burnIn.onSmallClockTopChanged, + modifier = Modifier.wrapContentSize() + ) + } + with(smartSpaceSection) { + SmartSpace( + burnInParams = burnIn.parameters, + onTopChanged = burnIn.onSmartspaceTopChanged, + ) } + with(mediaCarouselSection) { MediaCarousel() } + } + } - scene(largeClockScene) { - Column { - with(smartSpaceSection) { - SmartSpace( - burnInParams = burnIn.parameters, - onTopChanged = burnIn.onSmartspaceTopChanged, - ) - } - with(clockSection) { LargeClock(modifier = Modifier.fillMaxSize()) } - } + @Composable + private fun SceneScope.LargeClockWithSmartSpace(shouldOffSetClockToOneHalf: Boolean = false) { + val burnIn = rememberBurnIn(clockInteractor) + val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState() + + LaunchedEffect(isLargeClockVisible) { + if (isLargeClockVisible) { + burnIn.onSmallClockTopChanged(null) + } + } + + Column { + with(smartSpaceSection) { + SmartSpace( + burnInParams = burnIn.parameters, + onTopChanged = burnIn.onSmartspaceTopChanged, + ) + } + with(clockSection) { + LargeClock( + modifier = + Modifier.fillMaxSize().thenIf(shouldOffSetClockToOneHalf) { + // If we do not have a custom position animation, we want + // the clock to be on one half of the screen. + Modifier.offset { + IntOffset( + x = -clockSection.getClockCenteringDistance().toInt(), + y = 0, + ) + } + } + ) } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java index 324534fb766c..7986051de3e0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java @@ -105,7 +105,7 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase { when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false); when(mView.getUnpausedAlpha()).thenReturn(255); when(mShadeExpansionStateManager.addExpansionListener(any())).thenReturn( - new ShadeExpansionChangeEvent(0, false, false, 0)); + new ShadeExpansionChangeEvent(0, false, false)); mController = createUdfpsKeyguardViewController(); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt index 43266bfcbc55..a944afb70f38 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -88,7 +88,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) assertThat(scene).isEqualTo(CommunalScenes.Communal) fakeKeyguardTransitionRepository.sendTransitionSteps( @@ -158,7 +158,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) assertThat(scene).isEqualTo(CommunalScenes.Communal) fakeKeyguardTransitionRepository.sendTransitionSteps( @@ -179,7 +179,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) assertThat(scene).isEqualTo(CommunalScenes.Communal) fakeKeyguardTransitionRepository.sendTransitionSteps( @@ -207,7 +207,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { fun dockingOnLockscreen_forcesCommunal() = with(kosmos) { testScope.runTest { - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) val scene by collectLastValue(communalInteractor.desiredScene) // device is docked while on the lockscreen @@ -229,7 +229,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() = with(kosmos) { testScope.runTest { - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) val scene by collectLastValue(communalInteractor.desiredScene) // device is docked while on the lockscreen @@ -261,7 +261,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { // Device is dreaming and on communal. fakeKeyguardRepository.setDreaming(true) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) assertThat(scene).isEqualTo(CommunalScenes.Communal) @@ -278,7 +278,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { // Device is not dreaming and on communal. fakeKeyguardRepository.setDreaming(false) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) // Scene stays as Communal advanceTimeBy(SCREEN_TIMEOUT.milliseconds) @@ -293,7 +293,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { // Device is dreaming and on communal. fakeKeyguardRepository.setDreaming(true) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) assertThat(scene).isEqualTo(CommunalScenes.Communal) @@ -316,7 +316,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { // Device is on communal, but not dreaming. fakeKeyguardRepository.setDreaming(false) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) assertThat(scene).isEqualTo(CommunalScenes.Communal) @@ -338,7 +338,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { // Device is dreaming and on communal. fakeKeyguardRepository.setDreaming(true) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) assertThat(scene).isEqualTo(CommunalScenes.Communal) @@ -367,7 +367,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { // Device is dreaming and on communal. fakeKeyguardRepository.setDreaming(true) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) assertThat(scene).isEqualTo(CommunalScenes.Communal) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt index 43acf3197fb1..2d78a9b9d808 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt @@ -22,36 +22,26 @@ import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope -import com.android.systemui.scene.data.repository.sceneContainerRepository -import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags +import com.android.systemui.scene.shared.model.sceneDataSource import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class CommunalRepositoryImplTest : SysuiTestCase() { - private lateinit var underTest: CommunalRepositoryImpl private val kosmos = testKosmos() private val testScope = kosmos.testScope - private val sceneContainerRepository = kosmos.sceneContainerRepository - - @Before - fun setUp() { - underTest = createRepositoryImpl(false) - } - - private fun createRepositoryImpl(sceneContainerEnabled: Boolean): CommunalRepositoryImpl { - return CommunalRepositoryImpl( - testScope.backgroundScope, - kosmos.fakeSceneContainerFlags.apply { enabled = sceneContainerEnabled }, - sceneContainerRepository, + private val underTest by lazy { + CommunalRepositoryImpl( + kosmos.applicationCoroutineScope, + kosmos.sceneDataSource, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index 8e9d7690f215..e7ccde26e161 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -482,7 +482,7 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(desiredScene()).isEqualTo(CommunalScenes.Blank) val targetScene = CommunalScenes.Communal - communalRepository.setDesiredScene(targetScene) + communalRepository.changeScene(targetScene) desiredScene = collectLastValue(underTest.desiredScene) runCurrent() assertThat(desiredScene()).isEqualTo(targetScene) @@ -493,9 +493,9 @@ class CommunalInteractorTest : SysuiTestCase() { testScope.runTest { val targetScene = CommunalScenes.Communal - underTest.onSceneChanged(targetScene) + underTest.changeScene(targetScene) - val desiredScene = collectLastValue(communalRepository.desiredScene) + val desiredScene = collectLastValue(communalRepository.currentScene) runCurrent() assertThat(desiredScene()).isEqualTo(targetScene) } @@ -508,7 +508,7 @@ class CommunalInteractorTest : SysuiTestCase() { val desiredScene by collectLastValue(underTest.desiredScene) - underTest.onSceneChanged(CommunalScenes.Communal) + underTest.changeScene(CommunalScenes.Communal) assertThat(desiredScene).isEqualTo(CommunalScenes.Communal) kosmos.setCommunalAvailable(false) @@ -659,7 +659,7 @@ class CommunalInteractorTest : SysuiTestCase() { runCurrent() assertThat(isCommunalShowing()).isEqualTo(false) - underTest.onSceneChanged(CommunalScenes.Communal) + underTest.changeScene(CommunalScenes.Communal) isCommunalShowing = collectLastValue(underTest.isCommunalShowing) runCurrent() @@ -683,12 +683,12 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(isCommunalShowing).isFalse() // Verify scene changes (without the flag) to communal sets the value to true - underTest.onSceneChanged(CommunalScenes.Communal) + underTest.changeScene(CommunalScenes.Communal) runCurrent() assertThat(isCommunalShowing).isTrue() // Verify scene changes (without the flag) to blank sets the value back to false - underTest.onSceneChanged(CommunalScenes.Blank) + underTest.changeScene(CommunalScenes.Blank) runCurrent() assertThat(isCommunalShowing).isFalse() } @@ -704,7 +704,7 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(isCommunalShowing).isFalse() // Verify scene changes without the flag doesn't have any impact - underTest.onSceneChanged(CommunalScenes.Communal) + underTest.changeScene(CommunalScenes.Communal) runCurrent() assertThat(isCommunalShowing).isFalse() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt index 50b8da62b3f0..3a23e14e2777 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt @@ -158,7 +158,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { kosmos.setCommunalAvailable(true) communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED) - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED) } @@ -171,7 +171,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { goToCommunal() communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED) - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED) } @@ -184,13 +184,13 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { goToCommunal() communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED) } private suspend fun goToCommunal() { kosmos.setCommunalAvailable(true) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java index 5827671e22b8..6a86801cba90 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java @@ -376,7 +376,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { // Ensure correct expansion passed in. ShadeExpansionChangeEvent event = new ShadeExpansionChangeEvent( - expansion, /* expanded= */ false, /* tracking= */ true, dragDownAmount); + expansion, /* expanded= */ false, /* tracking= */ true); verify(mScrimController).expand(event); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java index 97052a84a60f..7cdd4781631f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java @@ -59,7 +59,7 @@ public class BouncerlessScrimControllerTest extends SysuiTestCase { final BouncerlessScrimController scrimController = new BouncerlessScrimController(mExecutor, mPowerManager); scrimController.addCallback(mCallback); - scrimController.expand(new ShadeExpansionChangeEvent(.5f, true, false, 0.0f)); + scrimController.expand(new ShadeExpansionChangeEvent(.5f, true, false)); mExecutor.runAllReady(); verify(mPowerManager).wakeUp(anyLong(), eq(PowerManager.WAKE_REASON_GESTURE), any()); verify(mCallback).onWakeup(); @@ -71,7 +71,7 @@ public class BouncerlessScrimControllerTest extends SysuiTestCase { new BouncerlessScrimController(mExecutor, mPowerManager); scrimController.addCallback(mCallback); final ShadeExpansionChangeEvent expansionEvent = - new ShadeExpansionChangeEvent(0.5f, false, false, 0.0f); + new ShadeExpansionChangeEvent(0.5f, false, false); scrimController.expand(expansionEvent); mExecutor.runAllReady(); verify(mCallback).onExpansion(eq(expansionEvent)); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt index ad2ae8b41af9..e6b30176d3ee 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt @@ -20,6 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic @@ -149,6 +150,38 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { } @Test + fun showAllNotifications_isTrue_whenLeaveShadeOpen() = + testScope.runTest { + val showAllNotifications by + collectLastValue(underTest.showAllNotifications(500.milliseconds, PRIMARY_BOUNCER)) + + sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true) + + keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) + keyguardTransitionRepository.sendTransitionStep(step(0.1f)) + + assertThat(showAllNotifications).isTrue() + keyguardTransitionRepository.sendTransitionStep(step(1f, TransitionState.FINISHED)) + assertThat(showAllNotifications).isFalse() + } + + @Test + fun showAllNotifications_isFalse_whenLeaveShadeIsNotOpen() = + testScope.runTest { + val showAllNotifications by + collectLastValue(underTest.showAllNotifications(500.milliseconds, PRIMARY_BOUNCER)) + + sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(false) + + keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) + keyguardTransitionRepository.sendTransitionStep(step(0.1f)) + + assertThat(showAllNotifications).isFalse() + keyguardTransitionRepository.sendTransitionStep(step(1f, TransitionState.FINISHED)) + assertThat(showAllNotifications).isFalse() + } + + @Test fun scrimBehindAlpha_doNotLeaveShadeOpen() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt index 751ac1d8b458..e9a825721f5f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt @@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardClockSwitch import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.authController +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository @@ -96,7 +97,7 @@ class LockscreenContentViewModelTest : SysuiTestCase() { shadeRepository.setShadeMode(ShadeMode.Split) kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE) - assertThat(underTest.areNotificationsVisible).isTrue() + assertThat(collectLastValue(underTest.areNotificationsVisible).invoke()).isTrue() } } @Test @@ -104,7 +105,7 @@ class LockscreenContentViewModelTest : SysuiTestCase() { with(kosmos) { testScope.runTest { kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL) - assertThat(underTest.areNotificationsVisible).isTrue() + assertThat(collectLastValue(underTest.areNotificationsVisible).invoke()).isTrue() } } @@ -113,7 +114,7 @@ class LockscreenContentViewModelTest : SysuiTestCase() { with(kosmos) { testScope.runTest { kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE) - assertThat(underTest.areNotificationsVisible).isFalse() + assertThat(collectLastValue(underTest.areNotificationsVisible).invoke()).isFalse() } } @@ -122,7 +123,8 @@ class LockscreenContentViewModelTest : SysuiTestCase() { with(kosmos) { testScope.runTest { shadeRepository.setShadeMode(ShadeMode.Split) - assertThat(underTest.shouldUseSplitNotificationShade).isTrue() + assertThat(collectLastValue(underTest.shouldUseSplitNotificationShade).invoke()) + .isTrue() } } @@ -131,16 +133,8 @@ class LockscreenContentViewModelTest : SysuiTestCase() { with(kosmos) { testScope.runTest { shadeRepository.setShadeMode(ShadeMode.Single) - assertThat(underTest.shouldUseSplitNotificationShade).isFalse() - } - } - - @Test - fun sceneKey() = - with(kosmos) { - testScope.runTest { - shadeRepository.setShadeMode(ShadeMode.Single) - assertThat(underTest.shouldUseSplitNotificationShade).isFalse() + assertThat(collectLastValue(underTest.shouldUseSplitNotificationShade).invoke()) + .isFalse() } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt index 16b68ccf097b..ad40f8eab4f6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt @@ -52,6 +52,7 @@ class PanelExpansionInteractorImplTest : SysuiTestCase() { private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor private val sceneInteractor = kosmos.sceneInteractor + private val shadeAnimationInteractor = kosmos.shadeAnimationInteractor private val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(Scenes.Lockscreen) @@ -112,6 +113,40 @@ class PanelExpansionInteractorImplTest : SysuiTestCase() { changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } + + @Test + @EnableSceneContainer + fun shouldHideStatusBarIconsWhenExpanded_goneScene() = + testScope.runTest { + underTest = kosmos.panelExpansionInteractorImpl + shadeAnimationInteractor.setIsLaunchingActivity(false) + changeScene(Scenes.Gone) + + assertThat(underTest.shouldHideStatusBarIconsWhenExpanded()).isFalse() + } + + @Test + @EnableSceneContainer + fun shouldHideStatusBarIconsWhenExpanded_lockscreenScene() = + testScope.runTest { + underTest = kosmos.panelExpansionInteractorImpl + shadeAnimationInteractor.setIsLaunchingActivity(false) + changeScene(Scenes.Lockscreen) + + assertThat(underTest.shouldHideStatusBarIconsWhenExpanded()).isTrue() + } + + @Test + @EnableSceneContainer + fun shouldHideStatusBarIconsWhenExpanded_activityLaunch() = + testScope.runTest { + underTest = kosmos.panelExpansionInteractorImpl + changeScene(Scenes.Gone) + shadeAnimationInteractor.setIsLaunchingActivity(true) + + assertThat(underTest.shouldHideStatusBarIconsWhenExpanded()).isFalse() + } + private fun TestScope.setUnlocked(isUnlocked: Boolean) { val isDeviceUnlocked by collectLastValue(deviceUnlockedInteractor.isDeviceUnlocked) deviceEntryRepository.setUnlocked(isUnlocked) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt index 31dacdd61151..52caa787bb2f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt @@ -18,15 +18,32 @@ package com.android.systemui.shade.domain.startable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository +import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor +import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope import com.android.systemui.res.R +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.fakeSceneDataSource +import com.android.systemui.shade.ShadeExpansionChangeEvent +import com.android.systemui.shade.ShadeExpansionListener import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.testKosmos +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -34,11 +51,15 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class ShadeStartableTest : SysuiTestCase() { - private val kosmos = testKosmos() private val testScope = kosmos.testScope private val shadeInteractor = kosmos.shadeInteractor + private val sceneInteractor = kosmos.sceneInteractor + private val shadeExpansionStateManager = kosmos.shadeExpansionStateManager + private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository + private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor private val fakeConfigurationRepository = kosmos.fakeConfigurationRepository + private val fakeSceneDataSource = kosmos.fakeSceneDataSource private val underTest = kosmos.shadeStartable @@ -59,4 +80,89 @@ class ShadeStartableTest : SysuiTestCase() { fakeConfigurationRepository.onAnyConfigurationChange() assertThat(shadeMode).isEqualTo(ShadeMode.Single) } + + @Test + @EnableSceneContainer + fun hydrateShadeExpansionStateManager() = + testScope.runTest { + val expansionListener = mock<ShadeExpansionListener>() + var latestChangeEvent: ShadeExpansionChangeEvent? = null + whenever(expansionListener.onPanelExpansionChanged(any())).thenAnswer { + latestChangeEvent = it.arguments[0] as ShadeExpansionChangeEvent + Unit + } + shadeExpansionStateManager.addExpansionListener(expansionListener) + + underTest.start() + + setUnlocked(true) + val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(Scenes.Gone) + ) + sceneInteractor.setTransitionState(transitionState) + + changeScene(Scenes.Gone, transitionState) + val currentScene by collectLastValue(sceneInteractor.currentScene) + assertThat(currentScene).isEqualTo(Scenes.Gone) + + assertThat(latestChangeEvent) + .isEqualTo( + ShadeExpansionChangeEvent( + fraction = 0f, + expanded = false, + tracking = false, + ) + ) + + changeScene(Scenes.Shade, transitionState) { progress -> + assertThat(latestChangeEvent?.fraction).isEqualTo(progress) + } + } + + private fun TestScope.setUnlocked(isUnlocked: Boolean) { + val isDeviceUnlocked by collectLastValue(deviceUnlockedInteractor.isDeviceUnlocked) + deviceEntryRepository.setUnlocked(isUnlocked) + runCurrent() + + assertThat(isDeviceUnlocked).isEqualTo(isUnlocked) + } + + private fun TestScope.changeScene( + toScene: SceneKey, + transitionState: MutableStateFlow<ObservableTransitionState>, + assertDuringProgress: ((progress: Float) -> Unit) = {}, + ) { + val currentScene by collectLastValue(sceneInteractor.currentScene) + val progressFlow = MutableStateFlow(0f) + transitionState.value = + ObservableTransitionState.Transition( + fromScene = checkNotNull(currentScene), + toScene = toScene, + progress = progressFlow, + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(true), + ) + runCurrent() + assertDuringProgress(progressFlow.value) + + progressFlow.value = 0.2f + runCurrent() + assertDuringProgress(progressFlow.value) + + progressFlow.value = 0.6f + runCurrent() + assertDuringProgress(progressFlow.value) + + progressFlow.value = 1f + runCurrent() + assertDuringProgress(progressFlow.value) + + transitionState.value = ObservableTransitionState.Idle(toScene) + fakeSceneDataSource.changeScene(toScene) + runCurrent() + assertDuringProgress(progressFlow.value) + + assertThat(currentScene).isEqualTo(toScene) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt index 5358a6dbb476..fa79e7fc9026 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt @@ -201,11 +201,38 @@ class AudioVolumeInteractorTest : SysuiTestCase() { } @Test - fun alarmStream_isNotMutable() { + fun streamNotAffectedByMute_isNotMutable() { with(kosmos) { - val isMutable = underTest.isMutable(AudioStream(AudioManager.STREAM_ALARM)) + testScope.runTest { + audioRepository.setIsAffectedByMute(audioStream, false) + val isMutable = underTest.isAffectedByMute(audioStream) + + assertThat(isMutable).isFalse() + } + } + } + + @Test + fun muteRingerStream_ringerMode_vibrate() { + with(kosmos) { + testScope.runTest { + val ringerMode by collectLastValue(audioRepository.ringerMode) + underTest.setMuted(AudioStream(AudioManager.STREAM_RING), true) - assertThat(isMutable).isFalse() + assertThat(ringerMode).isEqualTo(RingerMode(AudioManager.RINGER_MODE_VIBRATE)) + } + } + } + + @Test + fun unMuteRingerStream_ringerMode_normal() { + with(kosmos) { + testScope.runTest { + val ringerMode by collectLastValue(audioRepository.ringerMode) + underTest.setMuted(AudioStream(AudioManager.STREAM_RING), false) + + assertThat(ringerMode).isEqualTo(RingerMode(AudioManager.RINGER_MODE_NORMAL)) + } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt index 8e925573d40a..2cc1ad335535 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt @@ -84,8 +84,17 @@ class BottomBarViewModelTest : SysuiTestCase() { runCurrent() - verify(activityStarter).startActivity(capture(intentCaptor), eq(true), - capture(activityStartedCaptor)) + verify(activityStarter) + .startActivityDismissingKeyguard( + /* intent = */ capture(intentCaptor), + /* onlyProvisioned = */ eq(false), + /* dismissShade = */ eq(true), + /* disallowEnterPictureInPictureWhileLaunching = */ eq(false), + /* callback = */ capture(activityStartedCaptor), + /* flags = */ eq(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), + /* animationController = */ eq(null), + /* userHandle = */ eq(null), + ) assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_SOUND_SETTINGS) activityStartedCaptor.value.onActivityStarted(ActivityManager.START_SUCCESS) diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml index ef1a21f2fdf6..c988b4afcf74 100644 --- a/packages/SystemUI/res/layout/screenshot_shelf.xml +++ b/packages/SystemUI/res/layout/screenshot_shelf.xml @@ -28,7 +28,7 @@ android:elevation="4dp" android:background="@drawable/action_chip_container_background" android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal" - android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom" + android:layout_marginBottom="@dimen/screenshot_shelf_vertical_margin" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/actions_container" app:layout_constraintEnd_toEndOf="@+id/actions_container" @@ -38,14 +38,14 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal" - android:paddingEnd="@dimen/overlay_action_container_padding_end" + android:paddingHorizontal="@dimen/overlay_action_container_padding_end" android:paddingVertical="@dimen/overlay_action_container_padding_vertical" android:elevation="4dp" android:scrollbars="none" app:layout_constraintHorizontal_bias="0" app:layout_constraintWidth_percent="1.0" app:layout_constraintWidth_max="wrap" - app:layout_constraintStart_toEndOf="@+id/screenshot_preview_border" + app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="@id/actions_container_background"> <LinearLayout @@ -65,16 +65,16 @@ android:id="@+id/screenshot_preview_border" android:layout_width="0dp" android:layout_height="0dp" - android:layout_marginStart="16dp" + android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal" android:layout_marginTop="@dimen/overlay_border_width_neg" android:layout_marginEnd="@dimen/overlay_border_width_neg" - android:layout_marginBottom="14dp" + android:layout_marginBottom="@dimen/screenshot_shelf_vertical_margin" android:elevation="8dp" android:background="@drawable/overlay_border" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@id/screenshot_preview" app:layout_constraintEnd_toEndOf="@id/screenshot_preview" - app:layout_constraintBottom_toBottomOf="parent"/> + app:layout_constraintBottom_toTopOf="@id/actions_container"/> <ImageView android:id="@+id/screenshot_preview" android:layout_width="@dimen/overlay_x_scale" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 29f4942e8b4a..d2efccd546b6 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -448,6 +448,7 @@ <dimen name="overlay_action_container_padding_end">8dp</dimen> <dimen name="overlay_dismiss_button_tappable_size">48dp</dimen> <dimen name="overlay_dismiss_button_margin">8dp</dimen> + <dimen name="screenshot_shelf_vertical_margin">8dp</dimen> <!-- must be kept aligned with overlay_border_width_neg, below; overlay_border_width = overlay_border_width_neg * -1 --> <dimen name="overlay_border_width">4dp</dimen> diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 8f1a5f79687c..985f6c8bc59f 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -454,6 +454,7 @@ public class LockIconViewController implements Dumpable { final float scaleFactor = mAuthController.getScaleFactor(); final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor); if (KeyguardBottomAreaRefactor.isEnabled() || MigrateClocksToBlueprint.isEnabled()) { + // positioning in this case is handled by [DefaultDeviceEntrySection] mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding, scaledPadding); } else { diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java index b26be0c74ece..0cc3be2abf2c 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java @@ -99,9 +99,10 @@ public class SystemUIService extends Service { if (Build.IS_DEBUGGABLE) { // b/71353150 - looking for leaked binder proxies BinderInternal.nSetBinderProxyCountEnabled(true); - BinderInternal.nSetBinderProxyCountWatermarks(1000,900); + BinderInternal.nSetBinderProxyCountWatermarks( + /* high= */ 1000, /* low= */ 900, /* warning= */ 950); BinderInternal.setBinderProxyCountCallback( - new BinderInternal.BinderProxyLimitListener() { + new BinderInternal.BinderProxyCountEventListener() { @Override public void onLimitReached(int uid) { Slog.w(SystemUIApplication.TAG, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java index 1f5a0bf328af..be75e1035ea6 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java @@ -36,7 +36,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.android.internal.accessibility.dialog.AccessibilityTarget; -import com.android.internal.annotations.VisibleForTesting; import com.android.modules.expresslog.Counter; import com.android.systemui.Flags; import com.android.systemui.util.settings.SecureSettings; @@ -418,18 +417,11 @@ class MenuView extends FrameLayout implements onPositionChanged(); } - void incrementTexMetricForAllTargets(String metric) { + void incrementTexMetric(String metric) { if (!Flags.floatingMenuDragToEdit()) { return; } - for (AccessibilityTarget target : mTargetFeatures) { - incrementTexMetric(metric, target.getUid()); - } - } - - @VisibleForTesting - void incrementTexMetric(String metric, int uid) { - Counter.logIncrementWithUid(metric, uid); + Counter.logIncrement(metric); } private InstantInsetLayerDrawable getContainerViewInsetLayer() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java index 85bf784d623b..86279befc2ba 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java @@ -105,14 +105,14 @@ class MenuViewLayer extends FrameLayout implements * * <p>Defined in frameworks/proto_logging/stats/express/catalog/accessibility.cfg. */ - static final String TEX_METRIC_DISMISS = "accessibility.value_fab_shortcut_action_dismiss"; + static final String TEX_METRIC_DISMISS = "accessibility.value_fab_shortcut_dismiss"; /** * Counter indicating the FAB was dragged to the Edit action button. * * <p>Defined in frameworks/proto_logging/stats/express/catalog/accessibility.cfg. */ - static final String TEX_METRIC_EDIT = "accessibility.value_fab_shortcut_action_edit"; + static final String TEX_METRIC_EDIT = "accessibility.value_fab_shortcut_edit"; private final WindowManager mWindowManager; private final MenuView mMenuView; @@ -492,11 +492,11 @@ class MenuViewLayer extends FrameLayout implements } else { hideMenuAndShowMessage(); } - mMenuView.incrementTexMetricForAllTargets(TEX_METRIC_DISMISS); + mMenuView.incrementTexMetric(TEX_METRIC_DISMISS); } else if (id == R.id.action_edit && Flags.floatingMenuDragToEdit()) { gotoEditScreen(); - mMenuView.incrementTexMetricForAllTargets(TEX_METRIC_EDIT); + mMenuView.incrementTexMetric(TEX_METRIC_EDIT); } mDismissView.hide(); mDragToInteractView.hide(); diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index ef686f91b36a..4d328d6cb13f 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -72,7 +72,7 @@ constructor( .filterNotNull() // TODO(b/322787129): Also set a custom transition animation here to avoid the regular // slide-in animation when setting the scene programmatically - .onEach { nextScene -> communalInteractor.onSceneChanged(nextScene) } + .onEach { nextScene -> communalInteractor.changeScene(nextScene) } .launchIn(applicationScope) // TODO(b/322787129): re-enable once custom animations are in place @@ -129,7 +129,7 @@ constructor( .sample(keyguardInteractor.isDreaming, ::Pair) .collect { (shouldTimeout, isDreaming) -> if (isDreaming && shouldTimeout) { - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) } } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/Communal.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/Communal.kt new file mode 100644 index 000000000000..5e41a1b8a9b7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/Communal.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 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.communal.dagger + +import javax.inject.Qualifier + +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Communal diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt index 82d943796e2a..72dcb26b089a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt @@ -23,11 +23,19 @@ import com.android.systemui.communal.data.repository.CommunalRepositoryModule import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.widgets.CommunalWidgetModule import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.scene.shared.model.SceneContainerConfig +import com.android.systemui.scene.shared.model.SceneDataSource +import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import dagger.Binds import dagger.Module +import dagger.Provides +import kotlinx.coroutines.CoroutineScope @Module( includes = @@ -47,4 +55,24 @@ interface CommunalModule { fun bindEditWidgetsActivityStarter( starter: EditWidgetsActivityStarterImpl ): EditWidgetsActivityStarter + + @Binds + @Communal + fun bindCommunalSceneDataSource(@Communal delegator: SceneDataSourceDelegator): SceneDataSource + + companion object { + @Provides + @Communal + @SysUISingleton + fun providesCommunalSceneDataSourceDelegator( + @Application applicationScope: CoroutineScope + ): SceneDataSourceDelegator { + val config = + SceneContainerConfig( + sceneKeys = listOf(CommunalScenes.Blank, CommunalScenes.Communal), + initialSceneKey = CommunalScenes.Blank + ) + return SceneDataSourceDelegator(applicationScope, config) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt index 201ce832cc41..8bfd8d91dfca 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt @@ -18,11 +18,12 @@ package com.android.systemui.communal.data.repository import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey +import com.android.systemui.communal.dagger.Communal import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.scene.data.repository.SceneContainerRepository -import com.android.systemui.scene.shared.flag.SceneContainerFlags +import com.android.systemui.scene.shared.model.SceneDataSource import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -30,7 +31,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn @@ -38,16 +38,15 @@ import kotlinx.coroutines.flow.stateIn /** Encapsulates the state of communal mode. */ interface CommunalRepository { /** - * Target scene as requested by the underlying [SceneTransitionLayout] or through - * [setDesiredScene]. + * Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene]. */ - val desiredScene: StateFlow<SceneKey> + val currentScene: StateFlow<SceneKey> /** Exposes the transition state of the communal [SceneTransitionLayout]. */ val transitionState: StateFlow<ObservableTransitionState> /** Updates the requested scene. */ - fun setDesiredScene(desiredScene: SceneKey) + fun changeScene(toScene: SceneKey, transitionKey: TransitionKey? = null) /** * Updates the transition state of the hub [SceneTransitionLayout]. @@ -63,12 +62,10 @@ class CommunalRepositoryImpl @Inject constructor( @Background backgroundScope: CoroutineScope, - sceneContainerFlags: SceneContainerFlags, - sceneContainerRepository: SceneContainerRepository, + @Communal private val sceneDataSource: SceneDataSource, ) : CommunalRepository { - private val _desiredScene: MutableStateFlow<SceneKey> = MutableStateFlow(CommunalScenes.Default) - override val desiredScene: StateFlow<SceneKey> = _desiredScene.asStateFlow() + override val currentScene: StateFlow<SceneKey> = sceneDataSource.currentScene private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default) private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null) @@ -81,8 +78,8 @@ constructor( initialValue = defaultTransitionState, ) - override fun setDesiredScene(desiredScene: SceneKey) { - _desiredScene.value = desiredScene + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) { + sceneDataSource.changeScene(toScene, transitionKey) } /** diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index ada984db9a39..246d5d92f8b0 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -25,6 +25,7 @@ import android.os.UserManager import android.provider.Settings import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.communal.data.repository.CommunalMediaRepository import com.android.systemui.communal.data.repository.CommunalPrefsRepository @@ -80,6 +81,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.stateIn /** Encapsulates business-logic related to communal mode. */ @OptIn(ExperimentalCoroutinesApi::class) @@ -142,13 +144,12 @@ constructor( ) /** - * Target scene as requested by the underlying [SceneTransitionLayout] or through - * [onSceneChanged]. + * Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene]. * * If [isCommunalAvailable] is false, will return [CommunalScenes.Blank] */ val desiredScene: Flow<SceneKey> = - communalRepository.desiredScene.combine(isCommunalAvailable) { scene, available -> + communalRepository.currentScene.combine(isCommunalAvailable) { scene, available -> if (available) scene else CommunalScenes.Blank } @@ -239,10 +240,14 @@ constructor( * This will not be true while transitioning to the hub and will turn false immediately when a * swipe to exit the hub starts. */ - val isIdleOnCommunal: Flow<Boolean> = - communalRepository.transitionState.map { - it is ObservableTransitionState.Idle && it.scene == CommunalScenes.Communal - } + val isIdleOnCommunal: StateFlow<Boolean> = + communalRepository.transitionState + .map { it is ObservableTransitionState.Idle && it.scene == CommunalScenes.Communal } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = false, + ) /** * Flow that emits a boolean if any portion of the communal UI is visible at all. @@ -254,9 +259,12 @@ constructor( !(it is ObservableTransitionState.Idle && it.scene == CommunalScenes.Blank) } - /** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */ - fun onSceneChanged(newScene: SceneKey) { - communalRepository.setDesiredScene(newScene) + /** + * Asks for an asynchronous scene witch to [newScene], which will use the corresponding + * installed transition or the one specified by [transitionKey], if provided. + */ + fun changeScene(newScene: SceneKey, transitionKey: TransitionKey? = null) { + communalRepository.changeScene(newScene, transitionKey) } fun setEditModeOpen(isOpen: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index 531f19874b2a..095222a4b47a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -49,8 +49,8 @@ abstract class BaseCommunalViewModel( communalInteractor.signalUserInteraction() } - fun onSceneChanged(scene: SceneKey) { - communalInteractor.onSceneChanged(scene) + fun changeScene(scene: SceneKey) { + communalInteractor.changeScene(scene) } /** diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index 902133ddce1d..5f4b394a05b9 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -31,7 +31,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope -import com.android.app.tracing.coroutines.launch import com.android.compose.theme.LocalAndroidColorScheme import com.android.compose.theme.PlatformTheme import com.android.internal.logging.UiEventLogger @@ -150,7 +149,7 @@ constructor( private fun onEditDone() { try { - communalViewModel.onSceneChanged(CommunalScenes.Communal) + communalViewModel.changeScene(CommunalScenes.Communal) checkNotNull(windowManagerService).lockNow(/* options */ null) finish() } catch (e: RemoteException) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 19af371d1dfa..1ed4b503b43d 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -49,6 +49,7 @@ import com.android.systemui.common.data.CommonDataLayerModule; import com.android.systemui.communal.dagger.CommunalModule; import com.android.systemui.complication.dagger.ComplicationComponent; import com.android.systemui.controls.dagger.ControlsModule; +import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.SystemUser; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -89,6 +90,7 @@ import com.android.systemui.qs.footer.dagger.FooterActionsModule; import com.android.systemui.recents.Recents; import com.android.systemui.recordissue.RecordIssueModule; import com.android.systemui.retail.dagger.RetailModeModule; +import com.android.systemui.scene.shared.model.SceneContainerConfig; import com.android.systemui.scene.shared.model.SceneDataSource; import com.android.systemui.scene.shared.model.SceneDataSourceDelegator; import com.android.systemui.scene.ui.view.WindowRootViewComponent; @@ -165,6 +167,8 @@ import java.util.concurrent.Executor; import javax.inject.Named; +import kotlinx.coroutines.CoroutineScope; + /** * A dagger module for injecting components of System UI that are required by System UI. * @@ -402,6 +406,13 @@ public abstract class SystemUIModule { @ClassKey(SystemUISecondaryUserService.class) abstract Service bindsSystemUISecondaryUserService(SystemUISecondaryUserService service); + @Provides + @SysUISingleton + static SceneDataSourceDelegator providesSceneDataSourceDelegator( + @Application CoroutineScope applicationScope, SceneContainerConfig config) { + return new SceneDataSourceDelegator(applicationScope, config); + } + @Binds abstract SceneDataSource bindSceneDataSource(SceneDataSourceDelegator delegator); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java index 7f3b5eba96c6..926f7f1aee48 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java @@ -172,19 +172,18 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler { final float screenTravelPercentage = Math.abs(e1.getY() - e2.getY()) / mTouchSession.getBounds().height(); setPanelExpansion(mBouncerInitiallyShowing - ? screenTravelPercentage : 1 - screenTravelPercentage, dragDownAmount); + ? screenTravelPercentage : 1 - screenTravelPercentage); return true; } }; - private void setPanelExpansion(float expansion, float dragDownAmount) { + private void setPanelExpansion(float expansion) { mCurrentExpansion = expansion; ShadeExpansionChangeEvent event = new ShadeExpansionChangeEvent( /* fraction= */ mCurrentExpansion, /* expanded= */ mExpanded, - /* tracking= */ true, - /* dragDownPxAmount= */ dragDownAmount); + /* tracking= */ true); mCurrentScrimController.expand(event); } @@ -333,7 +332,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler { animation -> { float expansionFraction = (float) animation.getAnimatedValue(); float dragDownAmount = expansionFraction * expansionHeight; - setPanelExpansion(expansionFraction, dragDownAmount); + setPanelExpansion(expansionFraction); }); if (!mBouncerInitiallyShowing && targetExpansion == KeyguardBouncerConstants.EXPANSION_VISIBLE) { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt index 1b832d4ab98d..037c23b579c3 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt @@ -57,7 +57,7 @@ constructor( !keyguardUpdateMonitor.isEncryptedOrLockdown(userTracker.userId) if (showGlanceableHub) { toGlanceableHubTransitionViewModel.startTransition() - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) } else { toLockscreenTransitionViewModel.startTransition() } diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt index 17059097e053..f4998a7b8789 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt @@ -18,10 +18,10 @@ package com.android.systemui.haptics.qs import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.tracing.coroutines.launch import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.qs.tileimpl.QSTileViewImpl import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.launch class QSLongPressEffectViewBinder { @@ -31,16 +31,18 @@ class QSLongPressEffectViewBinder { fun bind( tile: QSTileViewImpl, + tileSpec: String?, effect: QSLongPressEffect?, ) { if (effect == null) return handle = tile.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.STARTED) { + repeatOnLifecycle(Lifecycle.State.CREATED) { effect.scope = this + val tag = "${tileSpec ?: "unknownTileSpec"}#LongPressEffect" - launch { + launch("$tag#progress") { effect.effectProgress.collect { progress -> progress?.let { if (it == 0f) { @@ -51,7 +53,7 @@ class QSLongPressEffectViewBinder { } } - launch { + launch("$tag#action") { effect.actionType.collect { action -> action?.let { when (it) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index fa845c7cf784..3da3e2f9cbcc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -29,6 +29,9 @@ import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.SceneTransitionLayout +import com.android.compose.animation.scene.transitions import com.android.internal.jank.InteractionJankMonitor import com.android.keyguard.KeyguardStatusView import com.android.keyguard.KeyguardStatusViewController @@ -109,6 +112,7 @@ constructor( private var rootViewHandle: DisposableHandle? = null private var indicationAreaHandle: DisposableHandle? = null + private val sceneKey = SceneKey("root-view-scene-key") var keyguardStatusViewController: KeyguardStatusViewController? = null get() { @@ -219,12 +223,25 @@ constructor( blueprints.mapNotNull { it as? ComposableLockscreenSceneBlueprint }.toSet() return ComposeView(context).apply { setContent { - LockscreenContent( - viewModel = viewModel, - blueprints = sceneBlueprints, - clockInteractor = clockInteractor - ) - .Content(modifier = Modifier.fillMaxSize()) + // STL is used solely to provide a SceneScope to enable us to invoke SceneScope + // composables. + SceneTransitionLayout( + currentScene = sceneKey, + onChangeScene = {}, + transitions = transitions {}, + ) { + scene(sceneKey) { + with( + LockscreenContent( + viewModel = viewModel, + blueprints = sceneBlueprints, + clockInteractor = clockInteractor + ) + ) { + Content(modifier = Modifier.fillMaxSize()) + } + } + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt index 88eadd7be066..b3d9a7670c8a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt @@ -92,7 +92,8 @@ constructor( walletController.setupWalletChangeObservers( callback, QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE, - QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE + QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE, + QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE ) withContext(backgroundDispatcher) { @@ -104,7 +105,8 @@ constructor( awaitClose { walletController.unregisterWalletChangeObservers( QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE, - QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE + QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE, + QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt index fa6efa504623..f763e6223a81 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt @@ -17,14 +17,17 @@ package com.android.systemui.keyguard.domain.backup +import android.app.backup.BackupDataInputStream import android.app.backup.SharedPreferencesBackupHelper import android.content.Context +import android.util.Log +import com.android.app.tracing.traceSection import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager import com.android.systemui.settings.UserFileManagerImpl /** Handles backup & restore for keyguard quick affordances. */ class KeyguardQuickAffordanceBackupHelper( - context: Context, + private val context: Context, userId: Int, ) : SharedPreferencesBackupHelper( @@ -34,4 +37,17 @@ class KeyguardQuickAffordanceBackupHelper( fileName = KeyguardQuickAffordanceSelectionManager.FILE_NAME, ) .getPath() - ) + ) { + + override fun restoreEntity(data: BackupDataInputStream?) { + Log.d(TAG, "Starting restore for ${data?.key} for user ${context.userId}") + traceSection("$TAG File restore: ${data?.key}") { + super.restoreEntity(data) + } + Log.d(TAG, "Finished restore for ${data?.key} for user ${context.userId}") + } + + companion object { + private const val TAG = "KeyguardQuickAffordanceBackupHelper" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt index d9f12c34c4f1..5906cfdda57e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.binder import android.content.Context +import android.util.DisplayMetrics import android.view.View import android.view.View.INVISIBLE import android.view.View.VISIBLE @@ -109,7 +110,7 @@ object KeyguardPreviewClockViewBinder { private fun applyClockDefaultConstraints(context: Context, constraints: ConstraintSet) { constraints.apply { constrainWidth(R.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT) - constrainHeight(R.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT) + constrainHeight(R.id.lockscreen_clock_view_large, ConstraintSet.MATCH_CONSTRAINT) val largeClockTopMargin = context.resources.getDimensionPixelSize(R.dimen.status_bar_height) + context.resources.getDimensionPixelSize( @@ -129,7 +130,29 @@ object KeyguardPreviewClockViewBinder { ConstraintSet.END ) - connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.lock_icon_view, TOP) + // In preview, we'll show UDFPS icon for UDFPS devices + // and nothing for non-UDFPS devices, + // but we need position of device entry icon to constrain clock + if (getConstraint(R.id.lock_icon_view) != null) { + connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.lock_icon_view, TOP) + } else { + // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection + val bottomPaddingPx = + context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) + val defaultDensity = + DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() / + DisplayMetrics.DENSITY_DEFAULT.toFloat() + val lockIconRadiusPx = (defaultDensity * 36).toInt() + val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx + connect( + R.id.lockscreen_clock_view_large, + BOTTOM, + PARENT_ID, + BOTTOM, + clockBottomMargin + ) + } + constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT) constrainHeight( R.id.lockscreen_clock_view, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index 4d9354dd1572..33052befadfc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -82,7 +82,6 @@ import kotlinx.coroutines.launch /** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */ @OptIn(ExperimentalCoroutinesApi::class) object KeyguardRootViewBinder { - @SuppressLint("ClickableViewAccessibility") @JvmStatic fun bind( @@ -102,14 +101,6 @@ object KeyguardRootViewBinder { ): DisposableHandle { var onLayoutChangeListener: OnLayoutChange? = null val childViews = mutableMapOf<Int, View>() - val statusViewId = R.id.keyguard_status_view - val burnInLayerId = R.id.burn_in_layer - val aodNotificationIconContainerId = R.id.aod_notification_icon_container - val largeClockId = R.id.lockscreen_clock_view_large - val indicationArea = R.id.keyguard_indication_area - val startButton = R.id.start_button - val endButton = R.id.end_button - val lockIcon = R.id.lock_icon_view if (KeyguardBottomAreaRefactor.isEnabled) { view.setOnTouchListener { _, event -> @@ -214,7 +205,7 @@ object KeyguardRootViewBinder { val px = state.value ?: return@collect when { state.isToOrFrom(KeyguardState.AOD) -> { - childViews[largeClockId]?.translationX = px + // Large Clock is not translated in the x direction childViews[burnInLayerId]?.translationX = px childViews[aodNotificationIconContainerId]?.translationX = px @@ -436,7 +427,7 @@ object KeyguardRootViewBinder { oldRight: Int, oldBottom: Int ) { - childViews[R.id.nssl_placeholder]?.let { notificationListPlaceholder -> + childViews[nsslPlaceholderId]?.let { notificationListPlaceholder -> // After layout, ensure the notifications are positioned correctly viewModel.onNotificationContainerBoundsChanged( notificationListPlaceholder.top.toFloat(), @@ -460,14 +451,14 @@ object KeyguardRootViewBinder { ) } } else { - childViews[R.id.keyguard_status_view]?.top ?: 0 + childViews[statusViewId]?.top ?: 0 } ) } } private fun isUserVisible(view: View): Boolean { - return view.id != R.id.burn_in_layer && + return view.id != burnInLayerId && view.visibility == VISIBLE && view.width > 0 && view.height > 0 @@ -589,6 +580,17 @@ object KeyguardRootViewBinder { private fun ViewPropertyAnimator.animateInIconTranslation(): ViewPropertyAnimator = setInterpolator(Interpolators.DECELERATE_QUINT).translationY(0f) + private val statusViewId = R.id.keyguard_status_view + private val burnInLayerId = R.id.burn_in_layer + private val aodNotificationIconContainerId = R.id.aod_notification_icon_container + private val largeClockId = R.id.lockscreen_clock_view_large + private val smallClockId = R.id.lockscreen_clock_view + private val indicationArea = R.id.keyguard_indication_area + private val startButton = R.id.start_button + private val endButton = R.id.end_button + private val lockIcon = R.id.lock_icon_view + private val nsslPlaceholderId = R.id.nssl_placeholder + private const val ID = "occluding_app_device_entry_unlock_msg" private const val AOD_ICONS_APPEAR_DURATION: Long = 200 } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt index 4c846e424f4b..29041d1665c3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt @@ -34,6 +34,7 @@ import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.KeyguardBottomAreaRefactor +import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder import com.android.systemui.keyguard.ui.view.DeviceEntryIconView @@ -72,7 +73,7 @@ constructor( override fun addViews(constraintLayout: ConstraintLayout) { if ( !KeyguardBottomAreaRefactor.isEnabled && - !DeviceEntryUdfpsRefactor.isEnabled && + !MigrateClocksToBlueprint.isEnabled && !DeviceEntryUdfpsRefactor.isEnabled ) { return diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt index c921fa7d7a19..8c6be989d8d9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt @@ -81,6 +81,10 @@ constructor( ) } + /** See [BouncerToGoneFlows#showAllNotifications] */ + val showAllNotifications: Flow<Boolean> = + bouncerToGoneFlows.showAllNotifications(TO_GONE_DURATION, ALTERNATE_BOUNCER) + /** Scrim alpha values */ val scrimAlpha: Flow<ScrimAlpha> = bouncerToGoneFlows.scrimAlpha(TO_GONE_DURATION, ALTERNATE_BOUNCER) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt index 924fc5d0333f..fe88b8169c89 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt @@ -32,6 +32,7 @@ import javax.inject.Inject import kotlin.time.Duration import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map @@ -63,6 +64,31 @@ constructor( } } + /** + * When the shade is expanded, make sure that all notifications can be seen immediately during a + * transition to GONE. This matters especially when the user has chosen to not show + * notifications on the lockscreen and then pulls down the shade, which presents them with an + * immediate auth prompt, followed by a notification animation. + */ + fun showAllNotifications(duration: Duration, from: KeyguardState): Flow<Boolean> { + var leaveShadeOpen = false + return animationFlow + .setup( + duration = duration, + from = from, + to = GONE, + ) + .sharedFlow( + duration = duration, + onStart = { leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide() }, + onStep = { if (leaveShadeOpen) 1f else 0f }, + onFinish = { 0f }, + onCancel = { 0f }, + ) + .map { it == 1f } + .distinctUntilChanged() + } + private fun createScrimAlphaFlow( duration: Duration, fromState: KeyguardState, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt index 1f80441492bc..36896f916e7d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt @@ -18,8 +18,10 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.res.Resources import com.android.keyguard.KeyguardClockSwitch +import com.android.keyguard.KeyguardClockSwitch.SMALL import com.android.systemui.biometrics.AuthController import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.res.R @@ -29,6 +31,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -42,6 +45,7 @@ constructor( private val authController: AuthController, val longPress: KeyguardLongPressViewModel, val shadeInteractor: ShadeInteractor, + @Application private val applicationScope: CoroutineScope, ) { private val clockSize = clockInteractor.clockSize @@ -50,11 +54,26 @@ constructor( val isLargeClockVisible: Boolean get() = clockSize.value == KeyguardClockSwitch.LARGE - val areNotificationsVisible: Boolean - get() = !isLargeClockVisible || shouldUseSplitNotificationShade + val shouldUseSplitNotificationShade: StateFlow<Boolean> = + shadeInteractor.shadeMode + .map { it == ShadeMode.Split } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = false, + ) - val shouldUseSplitNotificationShade: Boolean - get() = shadeInteractor.shadeMode.value == ShadeMode.Split + val areNotificationsVisible: StateFlow<Boolean> = + combine(clockSize, shouldUseSplitNotificationShade) { + clockSize, + shouldUseSplitNotificationShade -> + clockSize == SMALL || shouldUseSplitNotificationShade + } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = false, + ) fun getSmartSpacePaddingTop(resources: Resources): Int { return if (isLargeClockVisible) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt index 53f448826e80..05878265dd6d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt @@ -60,6 +60,10 @@ constructor( private var leaveShadeOpen: Boolean = false private var willRunDismissFromKeyguard: Boolean = false + /** See [BouncerToGoneFlows#showAllNotifications] */ + val showAllNotifications: Flow<Boolean> = + bouncerToGoneFlows.showAllNotifications(TO_GONE_DURATION, PRIMARY_BOUNCER) + val notificationAlpha: Flow<Float> = transitionAnimation.sharedFlow( duration = 200.milliseconds, 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 1aef9206d99f..8d3500a4f9f0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java @@ -29,6 +29,7 @@ import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.ReduceBrightColorsController; import com.android.systemui.qs.external.QSExternalModule; +import com.android.systemui.qs.panels.dagger.PanelsModule; import com.android.systemui.qs.pipeline.dagger.QSPipelineModule; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.qs.tiles.di.QSTilesModule; @@ -44,21 +45,22 @@ import com.android.systemui.statusbar.policy.SafetyController; import com.android.systemui.statusbar.policy.WalletController; import com.android.systemui.util.settings.SecureSettings; -import java.util.Map; - -import javax.inject.Named; - import dagger.Binds; import dagger.Module; import dagger.Provides; import dagger.multibindings.Multibinds; +import java.util.Map; + +import javax.inject.Named; + /** * Module for QS dependencies */ @Module(subcomponents = {QSFragmentComponent.class, QSSceneComponent.class}, includes = { MediaModule.class, + PanelsModule.class, QSExternalModule.class, QSFlagsModule.class, QSHostModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt new file mode 100644 index 000000000000..1307296dd2b7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.panels.dagger + +import com.android.systemui.qs.panels.data.repository.IconTilesRepository +import com.android.systemui.qs.panels.data.repository.IconTilesRepositoryImpl +import dagger.Binds +import dagger.Module + +@Module +interface PanelsModule { + @Binds fun bindIconTilesRepository(impl: IconTilesRepositoryImpl): IconTilesRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt new file mode 100644 index 000000000000..92f87e78f090 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 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.panels.data.repository + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.qs.pipeline.shared.TileSpec +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + +/** Repository for retrieving the list of [TileSpec] to be displayed as icons. */ +interface IconTilesRepository { + val iconTilesSpecs: Flow<Set<TileSpec>> +} + +@SysUISingleton +class IconTilesRepositoryImpl @Inject constructor() : IconTilesRepository { + + /** Set of toggleable tiles that are suitable for being shown as an icon. */ + override val iconTilesSpecs: Flow<Set<TileSpec>> = + flowOf( + setOf( + TileSpec.create("airplane"), + TileSpec.create("battery"), + TileSpec.create("cameratoggle"), + TileSpec.create("cast"), + TileSpec.create("color_correction"), + TileSpec.create("inversion"), + TileSpec.create("saver"), + TileSpec.create("dnd"), + TileSpec.create("flashlight"), + TileSpec.create("location"), + TileSpec.create("mictoggle"), + TileSpec.create("nfc"), + TileSpec.create("night"), + TileSpec.create("rotation") + ) + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt new file mode 100644 index 000000000000..367c67093605 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 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.panels.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.qs.panels.data.repository.IconTilesRepository +import com.android.systemui.qs.pipeline.shared.TileSpec +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Interactor for retrieving the list of [TileSpec] to be displayed as icons. */ +@SysUISingleton +class IconTilesInteractor @Inject constructor(private val repo: IconTilesRepository) { + val iconTilesSpecs: Flow<Set<TileSpec>> = repo.iconTilesSpecs +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 2360f27fc115..30044856a7d4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -620,7 +620,7 @@ open class QSTileViewImpl @JvmOverloads constructor( showRippleEffect = false setOnTouchListener(longPressEffect) if (!longPressEffectViewBinder.isBound) { - longPressEffectViewBinder.bind(this, longPressEffect) + longPressEffectViewBinder.bind(this, state.spec, longPressEffect) } } else { // Long-press effects might have been enabled before but the new state does not diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 1b7322592b41..e1b742e1e7f0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -182,12 +182,19 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override public boolean isAvailable() { + if (isWalletRoleAvailable()) { + return !mPackageManager.hasSystemFeature(FEATURE_CHROME_OS); + } return mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION) && !mPackageManager.hasSystemFeature(FEATURE_CHROME_OS) && mSecureSettings.getStringForUser(NFC_PAYMENT_DEFAULT_COMPONENT, UserHandle.USER_CURRENT) != null; } + private boolean isWalletRoleAvailable() { + return mHost.getUserId() == UserHandle.USER_SYSTEM && mController.isWalletRoleAvailable(); + } + @Nullable @Override public Intent getLongClickIntent() { diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt index 69dce83b7136..2fbcba977a91 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt @@ -20,9 +20,6 @@ package com.android.systemui.scene.shared.model import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow @@ -36,14 +33,10 @@ import kotlinx.coroutines.flow.stateIn * Delegates calls to a runtime-provided [SceneDataSource] or to a no-op implementation if a * delegate isn't set. */ -@SysUISingleton -class SceneDataSourceDelegator -@Inject -constructor( - @Application private val applicationScope: CoroutineScope, +class SceneDataSourceDelegator( + applicationScope: CoroutineScope, config: SceneContainerConfig, ) : SceneDataSource { - private val noOpDelegate = NoOpSceneDataSource(config.initialSceneKey) private val delegateMutable = MutableStateFlow<SceneDataSource>(noOpDelegate) @@ -82,6 +75,7 @@ constructor( ) : SceneDataSource { override val currentScene: StateFlow<SceneKey> = MutableStateFlow(initialSceneKey).asStateFlow() + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) = Unit } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index b796a206b5b4..047ecb42287b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -29,8 +29,6 @@ import static com.android.systemui.screenshot.LogConfig.logTag; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT; -import static java.util.Objects.requireNonNull; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.MainThread; @@ -41,7 +39,6 @@ import android.app.ActivityOptions; import android.app.ExitTransitionCoordinator; import android.app.ICompatCameraControlCallback; import android.app.Notification; -import android.app.assist.AssistContent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -73,7 +70,6 @@ import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManagerGlobal; -import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import android.window.WindowContext; @@ -81,11 +77,11 @@ import com.android.internal.app.ChooserActivity; import com.android.internal.logging.UiEventLogger; import com.android.internal.policy.PhoneWindow; import com.android.settingslib.applications.InterestingConfigChanges; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.clipboardoverlay.ClipboardOverlayController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.res.R; import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback; import com.android.systemui.screenshot.scroll.LongScreenshotActivity; @@ -218,17 +214,10 @@ public class ScreenshotController { // ScreenshotNotificationSmartActionsProvider. static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type"; static final String EXTRA_ID = "android:screenshot_id"; - static final String ACTION_TYPE_DELETE = "Delete"; - static final String ACTION_TYPE_SHARE = "Share"; - static final String ACTION_TYPE_EDIT = "Edit"; static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled"; - static final String EXTRA_OVERRIDE_TRANSITION = "android:screenshot_override_transition"; static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent"; static final String EXTRA_ACTION_INTENT_FILLIN = "android:screenshot_action_intent_fillin"; - static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id"; - static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification"; - static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip"; // From WizardManagerHelper.java private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete"; @@ -247,10 +236,10 @@ public class ScreenshotController { private final Executor mMainExecutor; private final ExecutorService mBgExecutor; private final BroadcastSender mBroadcastSender; + private final BroadcastDispatcher mBroadcastDispatcher; private final WindowManager mWindowManager; private final WindowManager.LayoutParams mWindowLayoutParams; - private final AccessibilityManager mAccessibilityManager; @Nullable private final ScreenshotSoundController mScreenshotSoundController; private final ScrollCaptureClient mScrollCaptureClient; @@ -278,7 +267,7 @@ public class ScreenshotController { private Animator mScreenshotAnimation; private RequestCallback mCurrentRequestCallback; private String mPackageName = ""; - private BroadcastReceiver mCopyBroadcastReceiver; + private final BroadcastReceiver mCopyBroadcastReceiver; // When false, the screenshot is taken without showing the ui. Note that this only applies to // external displays, as on the default one the UI should **always** be shown. @@ -300,6 +289,8 @@ public class ScreenshotController { @AssistedInject ScreenshotController( Context context, + DisplayManager displayManager, + WindowManager windowManager, FeatureFlags flags, ScreenshotViewProxy.Factory viewProxyFactory, ScreenshotActionsProvider.Factory actionsProviderFactory, @@ -315,6 +306,7 @@ public class ScreenshotController { ActivityManager activityManager, TimeoutHandler timeoutHandler, BroadcastSender broadcastSender, + BroadcastDispatcher broadcastDispatcher, ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider, ActionIntentExecutor actionExecutor, UserManager userManager, @@ -337,16 +329,17 @@ public class ScreenshotController { mScreenshotNotificationSmartActionsProvider = screenshotNotificationSmartActionsProvider; mBgExecutor = Executors.newSingleThreadExecutor(); mBroadcastSender = broadcastSender; + mBroadcastDispatcher = broadcastDispatcher; mScreenshotHandler = timeoutHandler; mScreenshotHandler.setDefaultTimeoutMillis(SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS); mDisplayId = displayId; - mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class)); + mDisplayManager = displayManager; + mWindowManager = windowManager; final Context displayContext = context.createDisplayContext(getDisplay()); mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null); - mWindowManager = mContext.getSystemService(WindowManager.class); mFlags = flags; mActionExecutor = actionExecutor; mUserManager = userManager; @@ -363,8 +356,6 @@ public class ScreenshotController { mViewProxy.requestDismissal(SCREENSHOT_INTERACTION_TIMEOUT); }); - mAccessibilityManager = AccessibilityManager.getInstance(mContext); - // Setup the window that we are going to use mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams(); mWindowLayoutParams.setTitle("ScreenshotAnimation"); @@ -390,9 +381,9 @@ public class ScreenshotController { } } }; - mContext.registerReceiver(mCopyBroadcastReceiver, new IntentFilter( - ClipboardOverlayController.COPY_OVERLAY_ACTION), - ClipboardOverlayController.SELF_PERMISSION, null, Context.RECEIVER_NOT_EXPORTED); + mBroadcastDispatcher.registerReceiver(mCopyBroadcastReceiver, new IntentFilter( + ClipboardOverlayController.COPY_OVERLAY_ACTION), null, null, + Context.RECEIVER_NOT_EXPORTED, ClipboardOverlayController.SELF_PERMISSION); mShowUIOnExternalDisplay = showUIOnExternalDisplay; } @@ -442,16 +433,6 @@ public class ScreenshotController { prepareViewForNewScreenshot(screenshot, oldPackageName); - if (mFlags.isEnabled(Flags.SCREENSHOT_METADATA) && screenshot.getTaskId() >= 0) { - mAssistContentRequester.requestAssistContent(screenshot.getTaskId(), - new AssistContentRequester.Callback() { - @Override - public void onAssistContentAvailable(AssistContent assistContent) { - screenshot.setContextUrl(assistContent.getWebUri()); - } - }); - } - if (!shouldShowUi()) { saveScreenshotInWorkerThread( screenshot.getUserHandle(), finisher, this::logSuccessOnActionsReady, @@ -567,7 +548,7 @@ public class ScreenshotController { * Release the constructed window context. */ private void releaseContext() { - mContext.unregisterReceiver(mCopyBroadcastReceiver); + mBroadcastDispatcher.unregisterReceiver(mCopyBroadcastReceiver); mContext.release(); } @@ -615,7 +596,7 @@ public class ScreenshotController { if (DEBUG_WINDOW) { Log.d(TAG, "setContentView: " + mViewProxy.getView()); } - setContentView(mViewProxy.getView()); + mWindow.setContentView(mViewProxy.getView()); } private void enqueueScrollCaptureRequest(UserHandle owner) { @@ -697,10 +678,8 @@ public class ScreenshotController { final ScrollCaptureResponse response = mLastScrollCaptureResponse; mViewProxy.showScrollChip(response.getPackageName(), /* onClick */ () -> { - DisplayMetrics displayMetrics = new DisplayMetrics(); - getDisplay().getRealMetrics(displayMetrics); - Bitmap newScreenshot = mImageCapture.captureDisplay(mDisplayId, - new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels)); + Bitmap newScreenshot = + mImageCapture.captureDisplay(mDisplayId, getFullScreenRect()); if (newScreenshot != null) { // delay starting scroll capture to make sure scrim is up before the app moves @@ -797,10 +776,6 @@ public class ScreenshotController { } } - private void setContentView(View contentView) { - mWindow.setContentView(contentView); - } - @MainThread private void attachWindow() { View decorView = mWindow.getDecorView(); @@ -912,12 +887,10 @@ public class ScreenshotController { public void onFinish() { } }; - Pair<ActivityOptions, ExitTransitionCoordinator> transition = - ActivityOptions.startSharedElementAnimation(mWindow, callbacks, null, - Pair.create(mViewProxy.getScreenshotPreview(), - ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); - return transition; + return ActivityOptions.startSharedElementAnimation(mWindow, callbacks, null, + Pair.create(mViewProxy.getScreenshotPreview(), + ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); } /** Reset screenshot view and then call onCompleteRunnable */ diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index cb2dba00890b..65e845749f9e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -90,7 +90,6 @@ import androidx.constraintlayout.widget.ConstraintLayout; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.UiEventLogger; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.res.R; import com.android.systemui.screenshot.scroll.ScrollCaptureController; import com.android.systemui.shared.system.InputChannelCompat; @@ -789,15 +788,8 @@ public class ScreenshotView extends FrameLayout implements mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED, 0, mPackageName); prepareSharedTransition(); - Intent shareIntent; - if (mFlags.isEnabled(Flags.SCREENSHOT_METADATA) && mScreenshotData != null - && mScreenshotData.getContextUrl() != null) { - shareIntent = ActionIntentCreator.INSTANCE.createShareWithText( - imageData.uri, mScreenshotData.getContextUrl().toString()); - } else { - shareIntent = ActionIntentCreator.INSTANCE.createShareWithSubject( - imageData.uri, imageData.subject); - } + Intent shareIntent = ActionIntentCreator.INSTANCE.createShareWithSubject( + imageData.uri, imageData.subject); mCallbacks.onAction(shareIntent, imageData.owner, false); }); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt new file mode 100644 index 000000000000..837a661230cb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 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.screenshot.data.model + +import android.app.ActivityTaskManager.RootTaskInfo + +/** Information about the tasks on a display. */ +data class DisplayContentModel( + /** The id of the display. */ + val displayId: Int, + /** Information about the current System UI state which can affect capture. */ + val systemUiState: SystemUiState, + /** A list of root tasks on the display, ordered from bottom to top along the z-axis */ + val rootTasks: List<RootTaskInfo>, +) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/model/SystemUiState.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/model/SystemUiState.kt new file mode 100644 index 000000000000..78be6bdda292 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/model/SystemUiState.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 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.screenshot.data.model + +/** Information about SystemUI state relevant to screenshot policy. */ +data class SystemUiState(val shadeExpanded: Boolean) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepository.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepository.kt new file mode 100644 index 000000000000..9c81b322a2b7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepository.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 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.screenshot.data.repository + +import com.android.systemui.screenshot.data.model.DisplayContentModel + +/** Provides information about tasks related to a display. */ +interface DisplayContentRepository { + /** Provides information about the tasks and content presented on a given display. */ + suspend fun getDisplayContent(displayId: Int): DisplayContentModel +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt new file mode 100644 index 000000000000..e9599dcb026d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 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.screenshot.data.repository + +import android.annotation.SuppressLint +import android.app.ActivityTaskManager +import android.app.IActivityTaskManager +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.screenshot.data.model.DisplayContentModel +import com.android.systemui.screenshot.data.model.SystemUiState +import com.android.systemui.screenshot.proxy.SystemUiProxy +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext + +/** + * Implements DisplayTaskRepository using [IActivityTaskManager], along with [ProfileTypeRepository] + * and [SystemUiProxy]. + */ +@SuppressLint("MissingPermission") +class DisplayContentRepositoryImpl +@Inject +constructor( + private val atmService: IActivityTaskManager, + private val systemUiProxy: SystemUiProxy, + @Background private val background: CoroutineDispatcher, +) : DisplayContentRepository { + + override suspend fun getDisplayContent(displayId: Int): DisplayContentModel { + return withContext(background) { + val rootTasks = atmService.getAllRootTaskInfosOnDisplay(displayId) + toDisplayTasksModel(displayId, rootTasks) + } + } + + private suspend fun toDisplayTasksModel( + displayId: Int, + rootTasks: List<ActivityTaskManager.RootTaskInfo>, + ): DisplayContentModel { + return DisplayContentModel( + displayId, + SystemUiState(systemUiProxy.isNotificationShadeExpanded()), + rootTasks + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt index 39b07e3a396a..bc71ab71b626 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt @@ -21,6 +21,8 @@ import com.android.systemui.screenshot.ImageCapture import com.android.systemui.screenshot.RequestProcessor import com.android.systemui.screenshot.ScreenshotPolicy import com.android.systemui.screenshot.ScreenshotRequestProcessor +import com.android.systemui.screenshot.data.repository.DisplayContentRepository +import com.android.systemui.screenshot.data.repository.DisplayContentRepositoryImpl import com.android.systemui.screenshot.data.repository.ProfileTypeRepository import com.android.systemui.screenshot.data.repository.ProfileTypeRepositoryImpl import dagger.Binds @@ -45,4 +47,8 @@ interface ScreenshotPolicyModule { return RequestProcessor(imageCapture, policyProvider.get()) } } + + @Binds + @SysUISingleton + fun bindDisplayContentRepository(impl: DisplayContentRepositoryImpl): DisplayContentRepository } diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 3169e9ccbbcb..33cf9ba4fa7c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -26,12 +26,14 @@ import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import com.android.compose.theme.PlatformTheme import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.communal.dagger.Communal import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.ui.compose.CommunalContainer import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.phone.SystemUIDialogFactory import com.android.systemui.util.kotlin.collectFlow @@ -52,6 +54,7 @@ constructor( private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val shadeInteractor: ShadeInteractor, private val powerManager: PowerManager, + @Communal private val dataSourceDelegator: SceneDataSourceDelegator, ) { /** The container view for the hub. This will not be initialized until [initView] is called. */ private var communalContainerView: View? = null @@ -125,6 +128,7 @@ constructor( PlatformTheme { CommunalContainer( viewModel = communalViewModel, + dataSourceDelegator = dataSourceDelegator, dialogFactory = dialogFactory, ) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java index c501d88b77ce..6bf5535b0761 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java @@ -27,6 +27,8 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.FrameLayout; +import com.android.systemui.scene.shared.flag.SceneContainerFlag; + /** The shade view. */ public final class NotificationPanelView extends FrameLayout { static final boolean DEBUG = false; @@ -41,14 +43,20 @@ public final class NotificationPanelView extends FrameLayout { public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); - setWillNotDraw(!DEBUG); - mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); + if (!SceneContainerFlag.isEnabled()) { + setWillNotDraw(!DEBUG); + mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); - setBackgroundColor(Color.TRANSPARENT); + setBackgroundColor(Color.TRANSPARENT); + } } @Override public void onRtlPropertiesChanged(int layoutDirection) { + if (SceneContainerFlag.isEnabled()) { + super.onRtlPropertiesChanged(layoutDirection); + return; + } if (mRtlChangeListener != null) { mRtlChangeListener.onRtlPropertielsChanged(layoutDirection); } @@ -56,14 +64,19 @@ public final class NotificationPanelView extends FrameLayout { @Override public boolean shouldDelayChildPressedState() { + if (SceneContainerFlag.isEnabled()) { + return super.shouldDelayChildPressedState(); + } return true; } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); - if (mCurrentPanelAlpha != 255) { - canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mAlphaPaint); + if (!SceneContainerFlag.isEnabled()) { + if (mCurrentPanelAlpha != 255) { + canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mAlphaPaint); + } } } @@ -83,6 +96,9 @@ public final class NotificationPanelView extends FrameLayout { @Override public boolean hasOverlappingRendering() { + if (SceneContainerFlag.isEnabled()) { + return super.hasOverlappingRendering(); + } return !mDozing; } @@ -102,6 +118,9 @@ public final class NotificationPanelView extends FrameLayout { @Override public boolean onInterceptTouchEvent(MotionEvent event) { + if (SceneContainerFlag.isEnabled()) { + return super.onInterceptTouchEvent(event); + } return mTouchHandler.onInterceptTouchEvent(event); } @@ -113,7 +132,9 @@ public final class NotificationPanelView extends FrameLayout { @Override public void dispatchConfigurationChanged(Configuration newConfig) { super.dispatchConfigurationChanged(newConfig); - mOnConfigurationChangedListener.onConfigurationChanged(newConfig); + if (!SceneContainerFlag.isEnabled()) { + mOnConfigurationChangedListener.onConfigurationChanged(newConfig); + } } /** Callback for right-to-left setting changes. */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index e4f5aeb5028c..0ddea307b33a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -982,6 +982,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump }); mAlternateBouncerInteractor = alternateBouncerInteractor; dumpManager.registerDumpable(this); + SceneContainerFlag.assertInLegacyMode(); } private void unlockAnimationFinished() { @@ -3555,9 +3556,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } @Override - public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) { + public void fadeOut(long startDelayMs, long durationMs, Runnable endAction) { mView.animate().cancel(); - return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration( + mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration( durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction( endAction); } @@ -4121,9 +4122,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Override public void updateExpansionAndVisibility() { - mShadeExpansionStateManager.onPanelExpansionChanged( - mExpandedFraction, isExpanded(), isTracking(), mExpansionDragDownAmountPx); - + if (!SceneContainerFlag.isEnabled()) { + mShadeExpansionStateManager.onPanelExpansionChanged( + mExpandedFraction, isExpanded(), isTracking()); + } updateVisibility(); } @@ -4159,7 +4161,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } /** Sends an external (e.g. Status Bar) intercept touch event to the Shade touch handler. */ - boolean handleExternalInterceptTouch(MotionEvent event) { + @Override + public boolean handleExternalInterceptTouch(MotionEvent event) { try { mUseExternalTouch = true; return mTouchHandler.onInterceptTouchEvent(event); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 59da8f1d5841..324dfdf32a3f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -52,6 +52,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.res.R; +import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -132,7 +133,8 @@ public class NotificationShadeWindowViewController implements Dumpable { private DragDownHelper mDragDownHelper; private boolean mExpandingBelowNotch; private final DockManager mDockManager; - private final NotificationPanelViewController mNotificationPanelViewController; + private final ShadeViewController mShadeViewController; + private final PanelExpansionInteractor mPanelExpansionInteractor; private final ShadeExpansionStateManager mShadeExpansionStateManager; private boolean mIsTrackingBarGesture = false; @@ -154,7 +156,8 @@ public class NotificationShadeWindowViewController implements Dumpable { DockManager dockManager, NotificationShadeDepthController depthController, NotificationShadeWindowView notificationShadeWindowView, - NotificationPanelViewController notificationPanelViewController, + ShadeViewController shadeViewController, + PanelExpansionInteractor panelExpansionInteractor, ShadeExpansionStateManager shadeExpansionStateManager, NotificationStackScrollLayoutController notificationStackScrollLayoutController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, @@ -187,7 +190,8 @@ public class NotificationShadeWindowViewController implements Dumpable { mStatusBarStateController = statusBarStateController; mView = notificationShadeWindowView; mDockManager = dockManager; - mNotificationPanelViewController = notificationPanelViewController; + mShadeViewController = shadeViewController; + mPanelExpansionInteractor = panelExpansionInteractor; mShadeExpansionStateManager = shadeExpansionStateManager; mDepthController = depthController; mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; @@ -374,7 +378,7 @@ public class NotificationShadeWindowViewController implements Dumpable { } if (!mIsTrackingBarGesture && isDown - && mNotificationPanelViewController.isFullyCollapsed()) { + && mPanelExpansionInteractor.isFullyCollapsed()) { float x = ev.getRawX(); float y = ev.getRawY(); if (mStatusBarViewController.touchIsWithinView(x, y)) { @@ -447,7 +451,7 @@ public class NotificationShadeWindowViewController implements Dumpable { } else { bouncerShowing = mService.isBouncerShowing(); } - if (mNotificationPanelViewController.isFullyExpanded() + if (mPanelExpansionInteractor.isFullyExpanded() && !bouncerShowing && !mStatusBarStateController.isDozing()) { if (mDragDownHelper.isDragDownEnabled()) { @@ -503,7 +507,7 @@ public class NotificationShadeWindowViewController implements Dumpable { cancellation.setAction(MotionEvent.ACTION_CANCEL); mStackScrollLayout.onInterceptTouchEvent(cancellation); if (!MigrateClocksToBlueprint.isEnabled()) { - mNotificationPanelViewController.handleExternalInterceptTouch(cancellation); + mShadeViewController.handleExternalInterceptTouch(cancellation); } cancellation.recycle(); } @@ -522,7 +526,7 @@ public class NotificationShadeWindowViewController implements Dumpable { // we still want to finish our drag down gesture when locking the screen handled |= mDragDownHelper.onTouchEvent(ev) || handled; } - if (!handled && mNotificationPanelViewController.handleExternalTouch(ev)) { + if (!handled && mShadeViewController.handleExternalTouch(ev)) { return true; } } else { @@ -611,7 +615,7 @@ public class NotificationShadeWindowViewController implements Dumpable { // Since NotificationStackScrollLayout is now a sibling of notification_panel, we need // to also ask NotificationPanelViewController directly, in order to process swipe up // events originating from notifications - if (mNotificationPanelViewController.handleExternalInterceptTouch(ev)) { + if (mShadeViewController.handleExternalInterceptTouch(ev)) { mShadeLogger.d("NSWVC: NPVC intercepted"); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index 037dc4d6ea21..07836e44e83d 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -127,7 +127,9 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl { @Override public void animateCollapseShade(int flags, boolean force, boolean delayed, float speedUpFactor) { - if (!force && mStatusBarStateController.getState() != StatusBarState.SHADE) { + int statusBarState = mStatusBarStateController.getState(); + if (!force && statusBarState != StatusBarState.SHADE + && statusBarState != StatusBarState.SHADE_LOCKED) { runPostCollapseActions(); return; } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionChangeEvent.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionChangeEvent.kt index 71dfafa09d52..d9c1f0a91d0c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionChangeEvent.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionChangeEvent.kt @@ -23,7 +23,5 @@ data class ShadeExpansionChangeEvent( /** Whether the panel should be considered expanded */ val expanded: Boolean, /** Whether the user is actively dragging the panel. */ - val tracking: Boolean, - /** The amount of pixels that the user has dragged during the expansion. */ - val dragDownPxAmount: Float + val tracking: Boolean ) diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt index df5ff5a8b1f5..359ddd86f115 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt @@ -18,13 +18,13 @@ package com.android.systemui.shade import android.annotation.IntDef import android.os.Trace -import android.os.Trace.TRACE_TAG_APP as TRACE_TAG import android.util.Log import androidx.annotation.FloatRange import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.Compile import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject +import android.os.Trace.TRACE_TAG_APP as TRACE_TAG /** * A class responsible for managing the notification panel's current state. @@ -42,7 +42,6 @@ class ShadeExpansionStateManager @Inject constructor() { @FloatRange(from = 0.0, to = 1.0) private var fraction: Float = 0f private var expanded: Boolean = false private var tracking: Boolean = false - private var dragDownPxAmount: Float = 0f /** * Adds a listener that will be notified when the panel expansion fraction has changed and @@ -53,7 +52,7 @@ class ShadeExpansionStateManager @Inject constructor() { @Deprecated("Use ShadeInteractor instead") fun addExpansionListener(listener: ShadeExpansionListener): ShadeExpansionChangeEvent { expansionListeners.add(listener) - return ShadeExpansionChangeEvent(fraction, expanded, tracking, dragDownPxAmount) + return ShadeExpansionChangeEvent(fraction, expanded, tracking) } /** Adds a listener that will be notified when the panel state has changed. */ @@ -76,8 +75,7 @@ class ShadeExpansionStateManager @Inject constructor() { fun onPanelExpansionChanged( @FloatRange(from = 0.0, to = 1.0) fraction: Float, expanded: Boolean, - tracking: Boolean, - dragDownPxAmount: Float + tracking: Boolean ) { require(!fraction.isNaN()) { "fraction cannot be NaN" } val oldState = state @@ -85,7 +83,6 @@ class ShadeExpansionStateManager @Inject constructor() { this.fraction = fraction this.expanded = expanded this.tracking = tracking - this.dragDownPxAmount = dragDownPxAmount var fullyClosed = true var fullyOpened = false @@ -111,7 +108,6 @@ class ShadeExpansionStateManager @Inject constructor() { "f=$fraction " + "expanded=$expanded " + "tracking=$tracking " + - "dragDownPxAmount=$dragDownPxAmount " + "${if (fullyOpened) " fullyOpened" else ""} " + if (fullyClosed) " fullyClosed" else "" ) @@ -124,8 +120,7 @@ class ShadeExpansionStateManager @Inject constructor() { } } - val expansionChangeEvent = - ShadeExpansionChangeEvent(fraction, expanded, tracking, dragDownPxAmount) + val expansionChangeEvent = ShadeExpansionChangeEvent(fraction, expanded, tracking) expansionListeners.forEach { it.onPanelExpansionChanged(expansionChangeEvent) } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt index 2d3833c55199..648d4b55370e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt @@ -162,9 +162,7 @@ abstract class ShadeModule { @Binds @SysUISingleton - abstract fun bindsShadeViewController( - notificationPanelViewController: NotificationPanelViewController - ): ShadeViewController + abstract fun bindsShadeViewController(shadeSurface: ShadeSurface): ShadeViewController @Binds @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt index d02c2154279b..7346a28dc7a7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt @@ -15,7 +15,6 @@ */ package com.android.systemui.shade -import android.view.ViewPropertyAnimator import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.shade.domain.interactor.ShadeBackActionInteractor import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor @@ -48,7 +47,7 @@ interface ShadeSurface : fun cancelAnimation() /** Animates the view from its current alpha to zero then runs the runnable. */ - fun fadeOut(startDelayMs: Long, durationMs: Long, endAction: Runnable): ViewPropertyAnimator + fun fadeOut(startDelayMs: Long, durationMs: Long, endAction: Runnable) /** Set whether the bouncer is showing. */ fun setBouncerShowing(bouncerShowing: Boolean) diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt new file mode 100644 index 000000000000..adb29287e40e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2024 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.shade + +import com.android.systemui.statusbar.GestureRecorder +import com.android.systemui.statusbar.phone.CentralSurfaces +import com.android.systemui.statusbar.policy.HeadsUpManager +import javax.inject.Inject + +class ShadeSurfaceImpl @Inject constructor() : ShadeSurface, ShadeViewControllerEmptyImpl() { + override fun initDependencies( + centralSurfaces: CentralSurfaces, + recorder: GestureRecorder, + hideExpandedRunnable: Runnable, + headsUpManager: HeadsUpManager + ) {} + + override fun cancelPendingCollapse() { + // Do nothing + } + + override fun cancelAnimation() { + // Do nothing + } + + override fun fadeOut(startDelayMs: Long, durationMs: Long, endAction: Runnable) { + // Do nothing + } + + override fun setBouncerShowing(bouncerShowing: Boolean) { + // Do nothing + } + + override fun setTouchAndAnimationDisabled(disabled: Boolean) { + // TODO(b/322197941): determine if still needed + } + + override fun setWillPlayDelayedDozeAmountAnimation(willPlay: Boolean) { + // TODO(b/322494538): determine if still needed + } + + override fun setDozing(dozing: Boolean, animate: Boolean) { + // Do nothing + } + + override fun setImportantForAccessibility(mode: Int) { + // Do nothing + } + + override fun resetTranslation() { + // Do nothing + } + + override fun resetAlpha() { + // Do nothing + } + + override fun onScreenTurningOn() { + // Do nothing + } + + override fun onThemeChanged() { + // Do nothing + } + + override fun updateExpansionAndVisibility() { + // Do nothing + } + + override fun updateResources() { + // Do nothing + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt index 5b2377f7f610..4e1edd33ecf4 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt @@ -33,14 +33,12 @@ interface ShadeViewController { /** Returns whether the shade's top level view is enabled. */ @Deprecated("No longer supported. Do not add new calls to this.") val isViewEnabled: Boolean - /** Returns whether status bar icons should be hidden when the shade is expanded. */ - fun shouldHideStatusBarIconsWhenExpanded(): Boolean - /** If the latency tracker is enabled, begins tracking expand latency. */ @Deprecated("No longer supported. Do not add new calls to this.") fun startExpandLatencyTracking() /** Sets the alpha value of the shade to a value between 0 and 255. */ + @Deprecated("No longer supported. Do not add new calls to this.") fun setAlpha(alpha: Int, animate: Boolean) /** @@ -48,6 +46,7 @@ interface ShadeViewController { * * @see .setAlpha */ + @Deprecated("No longer supported. Do not add new calls to this.") fun setAlphaChangeAnimationEndAction(r: Runnable) /** Sets Qs ScrimEnabled and updates QS state. */ @@ -61,7 +60,7 @@ interface ShadeViewController { @Deprecated("Does nothing when scene container is enabled.") fun updateSystemUiStateFlags() /** Ensures that the touchable region is updated. */ - fun updateTouchableRegion() + @Deprecated("No longer supported. Do not add new calls to this.") fun updateTouchableRegion() /** * Sends an external (e.g. Status Bar) touch event to the Shade touch handler. @@ -72,6 +71,8 @@ interface ShadeViewController { */ fun handleExternalTouch(event: MotionEvent): Boolean + fun handleExternalInterceptTouch(event: MotionEvent): Boolean + /** * Triggered when an input focus transfer gesture has started. * diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt index e037c701a165..0c41efd513ad 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt @@ -28,7 +28,7 @@ import javax.inject.Inject import kotlinx.coroutines.flow.flowOf /** Empty implementation of ShadeViewController for variants with no shade. */ -class ShadeViewControllerEmptyImpl @Inject constructor() : +open class ShadeViewControllerEmptyImpl @Inject constructor() : ShadeViewController, ShadeBackActionInteractor, ShadeLockscreenInteractor, @@ -81,6 +81,10 @@ class ShadeViewControllerEmptyImpl @Inject constructor() : override fun handleExternalTouch(event: MotionEvent): Boolean { return false } + override fun handleExternalInterceptTouch(event: MotionEvent): Boolean { + return false + } + override fun startInputFocusTransfer() {} override fun cancelInputFocusTransfer() {} override fun finishInputFocusTransfer(velocity: Float) {} diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt index 661130317854..dfdf2ad564db 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt @@ -72,4 +72,8 @@ interface PanelExpansionInteractor { /** Returns the StatusBarState. Note: System UI was formerly known simply as Status Bar. */ @Deprecated("Use SceneInteractor or ShadeInteractor instead") val barState: Int + + /** Returns whether status bar icons should be hidden when the shade is expanded. */ + @Deprecated("No longer supported. Do not add new calls to this.") + fun shouldHideStatusBarIconsWhenExpanded(): Boolean } diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt index 561d0bc8e6ef..58bcd2e0d7eb 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt @@ -69,27 +69,21 @@ constructor( state.fromScene == Scenes.Gone -> if (state.toScene.isExpandable()) { // Moving from Gone to a scene that can animate-expand has a - // panel - // expansion - // that tracks with the transition. + // panel expansion that tracks with the transition. state.progress } else { // Moving from Gone to a scene that doesn't animate-expand - // immediately makes - // the panel fully expanded. + // immediately makes the panel fully expanded. flowOf(1f) } state.toScene == Scenes.Gone -> if (state.fromScene.isExpandable()) { // Moving to Gone from a scene that can animate-expand has a - // panel - // expansion - // that tracks with the transition. + // panel expansion that tracks with the transition. state.progress.map { 1 - it } } else { // Moving to Gone from a scene that doesn't animate-expand - // immediately makes - // the panel fully collapsed. + // immediately makes the panel fully collapsed. flowOf(0f) } else -> flowOf(1f) @@ -126,6 +120,15 @@ constructor( override val barState get() = statusBarStateController.state + @Deprecated("No longer supported. Do not add new calls to this.") + override fun shouldHideStatusBarIconsWhenExpanded(): Boolean { + if (shadeAnimationInteractor.isLaunchingActivity.value) { + return false + } + // TODO(b/325936094) if a HUN is showing, return false + return sceneInteractor.currentScene.value == Scenes.Lockscreen + } + private fun SceneKey.isExpandable(): Boolean { return this == Scenes.Shade || this == Scenes.QuickSettings } diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt index d8216dcfdec6..f3802da9bc9b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt @@ -23,13 +23,20 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer import com.android.systemui.log.dagger.ShadeTouchLog +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.shade.TouchLogger.Companion.logTouchesTo import com.android.systemui.shade.data.repository.ShadeRepository +import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.shade.transition.ScrimShadeTransitionController import com.android.systemui.statusbar.policy.SplitShadeStateController import javax.inject.Inject +import javax.inject.Provider import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch @@ -43,23 +50,44 @@ constructor( @ShadeTouchLog private val touchLog: LogBuffer, private val configurationRepository: ConfigurationRepository, private val shadeRepository: ShadeRepository, - private val controller: SplitShadeStateController, + private val splitShadeStateController: SplitShadeStateController, private val scrimShadeTransitionController: ScrimShadeTransitionController, + private val sceneInteractorProvider: Provider<SceneInteractor>, + private val panelExpansionInteractorProvider: Provider<PanelExpansionInteractor>, + private val shadeExpansionStateManager: ShadeExpansionStateManager, ) : CoreStartable { override fun start() { hydrateShadeMode() + hydrateShadeExpansionStateManager() logTouchesTo(touchLog) scrimShadeTransitionController.init() } + private fun hydrateShadeExpansionStateManager() { + if (SceneContainerFlag.isEnabled) { + combine( + panelExpansionInteractorProvider.get().legacyPanelExpansion, + sceneInteractorProvider.get().isTransitionUserInputOngoing, + ) { panelExpansion, tracking -> + shadeExpansionStateManager.onPanelExpansionChanged( + fraction = panelExpansion, + expanded = panelExpansion > 0f, + tracking = tracking, + ) + }.launchIn(applicationScope) + } + } + private fun hydrateShadeMode() { applicationScope.launch { configurationRepository.onAnyConfigurationChange // Force initial collection. .onStart { emit(Unit) } .map { applicationContext.resources } - .map { resources -> controller.shouldUseSplitNotificationShade(resources) } + .map { resources -> + splitShadeStateController.shouldUseSplitNotificationShade(resources) + } .collect { isSplitShade -> shadeRepository.setShadeMode( if (isSplitShade) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt index 151e28903fba..e38e53d67f61 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt @@ -17,28 +17,20 @@ package com.android.systemui.shade.transition import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager -import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.PanelState import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionStateManager -import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.statusbar.phone.ScrimController -import dagger.Lazy import java.io.PrintWriter import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch /** Controls the scrim properties during the shade expansion transition on non-lockscreen. */ @SysUISingleton class ScrimShadeTransitionController @Inject constructor( - @Application private val applicationScope: CoroutineScope, private val shadeExpansionStateManager: ShadeExpansionStateManager, - private val panelExpansionInteractor: Lazy<PanelExpansionInteractor>, private val dumpManager: DumpManager, private val scrimController: ScrimController, ) { @@ -47,32 +39,17 @@ constructor( private var currentPanelState: Int? = null fun init() { - if (SceneContainerFlag.isEnabled) { - applicationScope.launch { - panelExpansionInteractor.get().legacyPanelExpansion.collect { panelExpansion -> - onPanelExpansionChanged( - ShadeExpansionChangeEvent( - fraction = panelExpansion, - expanded = panelExpansion > 0f, - tracking = true, - dragDownPxAmount = 0f, - ) - ) - } - } - } else { - val currentState = - shadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged) - onPanelExpansionChanged(currentState) - shadeExpansionStateManager.addStateListener(this::onPanelStateChanged) - } + val currentState = + shadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged) + onPanelExpansionChanged(currentState) + shadeExpansionStateManager.addStateListener(this::onPanelStateChanged) dumpManager.registerDumpable( ScrimShadeTransitionController::class.java.simpleName, this::dump ) } - fun onPanelStateChanged(@PanelState state: Int) { + private fun onPanelStateChanged(@PanelState state: Int) { currentPanelState = state onStateChanged() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java index 8d7fc98164c0..acb5339b1a0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java @@ -19,8 +19,6 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.widget.TextView; -import com.android.settingslib.WirelessUtils; - /** Shows the operator name */ public class OperatorNameView extends TextView { private boolean mDemoMode; @@ -41,13 +39,14 @@ public class OperatorNameView extends TextView { mDemoMode = demoMode; } - void update(boolean showOperatorName, + void update( + boolean showOperatorName, boolean hasMobile, + boolean airplaneMode, OperatorNameViewController.SubInfo sub ) { setVisibility(showOperatorName ? VISIBLE : GONE); - boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext); if (!hasMobile || airplaneMode) { setText(null); setVisibility(GONE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java index 8afc72f08656..6e7d8f485195 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java @@ -16,11 +16,9 @@ package com.android.systemui.statusbar; -import android.annotation.NonNull; import android.os.Bundle; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.view.View; @@ -28,47 +26,60 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.demomode.DemoModeCommandReceiver; import com.android.systemui.plugins.DarkIconDispatcher; -import com.android.systemui.statusbar.connectivity.IconState; -import com.android.systemui.statusbar.connectivity.NetworkController; -import com.android.systemui.statusbar.connectivity.SignalCallback; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor; +import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.ViewController; +import com.android.systemui.util.kotlin.JavaAdapter; import javax.inject.Inject; +import kotlinx.coroutines.Job; + /** Controller for {@link OperatorNameView}. */ public class OperatorNameViewController extends ViewController<OperatorNameView> { private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name"; private final DarkIconDispatcher mDarkIconDispatcher; - private final NetworkController mNetworkController; private final TunerService mTunerService; private final TelephonyManager mTelephonyManager; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final CarrierConfigTracker mCarrierConfigTracker; + private final AirplaneModeInteractor mAirplaneModeInteractor; + private final SubscriptionManagerProxy mSubscriptionManagerProxy; + private final JavaAdapter mJavaAdapter; + + private Job mAirplaneModeJob; private OperatorNameViewController(OperatorNameView view, DarkIconDispatcher darkIconDispatcher, - NetworkController networkController, TunerService tunerService, TelephonyManager telephonyManager, KeyguardUpdateMonitor keyguardUpdateMonitor, - CarrierConfigTracker carrierConfigTracker) { + CarrierConfigTracker carrierConfigTracker, + AirplaneModeInteractor airplaneModeInteractor, + SubscriptionManagerProxy subscriptionManagerProxy, + JavaAdapter javaAdapter) { super(view); mDarkIconDispatcher = darkIconDispatcher; - mNetworkController = networkController; mTunerService = tunerService; mTelephonyManager = telephonyManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mCarrierConfigTracker = carrierConfigTracker; + mAirplaneModeInteractor = airplaneModeInteractor; + mSubscriptionManagerProxy = subscriptionManagerProxy; + mJavaAdapter = javaAdapter; } @Override protected void onViewAttached() { mDarkIconDispatcher.addDarkReceiver(mDarkReceiver); - mNetworkController.addCallback(mSignalCallback); + mAirplaneModeJob = + mJavaAdapter.alwaysCollectFlow( + mAirplaneModeInteractor.isAirplaneMode(), + (isAirplaneMode) -> update()); mTunerService.addTunable(mTunable, KEY_SHOW_OPERATOR_NAME); mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); } @@ -76,7 +87,7 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> @Override protected void onViewDetached() { mDarkIconDispatcher.removeDarkReceiver(mDarkReceiver); - mNetworkController.removeCallback(mSignalCallback); + mAirplaneModeJob.cancel(null); mTunerService.removeTunable(mTunable); mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); } @@ -87,11 +98,17 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> mCarrierConfigTracker .getShowOperatorNameInStatusBarConfig(defaultSubInfo.getSubId()) && (mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0); - mView.update(showOperatorName, mTelephonyManager.isDataCapable(), getDefaultSubInfo()); + mView.update( + showOperatorName, + mTelephonyManager.isDataCapable(), + mAirplaneModeInteractor.isAirplaneMode().getValue(), + getDefaultSubInfo() + ); } private SubInfo getDefaultSubInfo() { - int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId(); + int defaultSubId = mSubscriptionManagerProxy.getDefaultDataSubscriptionId(); + SubscriptionInfo sI = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(defaultSubId); return new SubInfo( sI.getSubscriptionId(), @@ -103,36 +120,44 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> /** Factory for constructing an {@link OperatorNameViewController}. */ public static class Factory { private final DarkIconDispatcher mDarkIconDispatcher; - private final NetworkController mNetworkController; private final TunerService mTunerService; private final TelephonyManager mTelephonyManager; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final CarrierConfigTracker mCarrierConfigTracker; + private final AirplaneModeInteractor mAirplaneModeInteractor; + private final SubscriptionManagerProxy mSubscriptionManagerProxy; + private final JavaAdapter mJavaAdapter; @Inject public Factory(DarkIconDispatcher darkIconDispatcher, - NetworkController networkController, TunerService tunerService, TelephonyManager telephonyManager, KeyguardUpdateMonitor keyguardUpdateMonitor, - CarrierConfigTracker carrierConfigTracker) { + CarrierConfigTracker carrierConfigTracker, + AirplaneModeInteractor airplaneModeInteractor, + SubscriptionManagerProxy subscriptionManagerProxy, + JavaAdapter javaAdapter) { mDarkIconDispatcher = darkIconDispatcher; - mNetworkController = networkController; mTunerService = tunerService; mTelephonyManager = telephonyManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mCarrierConfigTracker = carrierConfigTracker; + mAirplaneModeInteractor = airplaneModeInteractor; + mSubscriptionManagerProxy = subscriptionManagerProxy; + mJavaAdapter = javaAdapter; } /** Create an {@link OperatorNameViewController}. */ public OperatorNameViewController create(OperatorNameView view) { return new OperatorNameViewController(view, mDarkIconDispatcher, - mNetworkController, mTunerService, mTelephonyManager, mKeyguardUpdateMonitor, - mCarrierConfigTracker); + mCarrierConfigTracker, + mAirplaneModeInteractor, + mSubscriptionManagerProxy, + mJavaAdapter); } } @@ -149,13 +174,6 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> (area, darkIntensity, tint) -> mView.setTextColor(DarkIconDispatcher.getTint(area, mView, tint)); - private final SignalCallback mSignalCallback = new SignalCallback() { - @Override - public void setIsAirplaneMode(@NonNull IconState icon) { - update(); - } - }; - private final TunerService.Tunable mTunable = (key, newValue) -> update(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index f960fcafafc6..e5b64970c5b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -35,9 +35,11 @@ import com.android.systemui.dump.DumpHandler; import com.android.systemui.dump.DumpManager; import com.android.systemui.media.controls.domain.pipeline.MediaDataManager; import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shade.ShadeSurface; +import com.android.systemui.shade.ShadeSurfaceImpl; import com.android.systemui.shade.carrier.ShadeCarrierGroupController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationClickNotifier; @@ -59,6 +61,8 @@ import com.android.systemui.statusbar.phone.StatusBarIconList; import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; import com.android.systemui.statusbar.policy.KeyguardStateController; +import javax.inject.Provider; + import dagger.Binds; import dagger.Lazy; import dagger.Module; @@ -178,9 +182,20 @@ public interface CentralSurfacesDependenciesModule { * The {@link com.android.systemui.shade.ShadeViewController} interface is bound in * {@link com.android.systemui.shade.ShadeModule} so others can access it. */ - @Binds + @Provides @SysUISingleton - ShadeSurface provideShadeSurface(NotificationPanelViewController impl); + static ShadeSurface provideShadeSurface( + SceneContainerFlags sceneContainerFlags, + Provider<ShadeSurfaceImpl> sceneContainerOn, + Provider<NotificationPanelViewController> sceneContainerOff) { + if (sceneContainerFlags.isEnabled()) { + return sceneContainerOn.get(); + } else { + return sceneContainerOff.get(); + } + + } + /** */ @Binds diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 7c718645bc74..4c66f6617312 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -23,7 +23,9 @@ import androidx.core.animation.ObjectAnimator import com.android.app.animation.Interpolators import com.android.app.animation.InterpolatorsAndroidX import com.android.systemui.Dumpable +import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionChangeEvent @@ -47,11 +49,14 @@ import java.io.PrintWriter import javax.inject.Inject import kotlin.math.max import kotlin.math.min +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch @SysUISingleton class NotificationWakeUpCoordinator @Inject constructor( + @Application applicationScope: CoroutineScope, dumpManager: DumpManager, private val mHeadsUpManager: HeadsUpManager, private val statusBarStateController: StatusBarStateController, @@ -60,6 +65,7 @@ constructor( private val screenOffAnimationController: ScreenOffAnimationController, private val logger: NotificationWakeUpCoordinatorLogger, private val notifsKeyguardInteractor: NotificationsKeyguardInteractor, + private val communalInteractor: CommunalInteractor, ) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, @@ -201,6 +207,13 @@ constructor( } } ) + applicationScope.launch { + communalInteractor.isIdleOnCommunal.collect { + if (!overrideDozeAmountIfCommunalShowing()) { + maybeClearHardDozeAmountOverrideHidingNotifs() + } + } + } } fun setStackScroller(stackScrollerController: NotificationStackScrollLayoutController) { @@ -302,6 +315,10 @@ constructor( return } + if (overrideDozeAmountIfCommunalShowing()) { + return + } + if (clearHardDozeAmountOverride()) { return } @@ -311,9 +328,12 @@ constructor( private fun setHardDozeAmountOverride(dozing: Boolean, source: String) { logger.logSetDozeAmountOverride(dozing = dozing, source = source) + val previousOverride = hardDozeAmountOverride hardDozeAmountOverride = if (dozing) 1f else 0f hardDozeAmountOverrideSource = source - updateDozeAmount() + if (previousOverride != hardDozeAmountOverride) { + updateDozeAmount() + } } private fun clearHardDozeAmountOverride(): Boolean { @@ -434,6 +454,11 @@ constructor( return } + if (overrideDozeAmountIfCommunalShowing()) { + this.state = newState + return + } + maybeClearHardDozeAmountOverrideHidingNotifs() this.state = newState @@ -471,6 +496,18 @@ constructor( return false } + private fun overrideDozeAmountIfCommunalShowing(): Boolean { + if (communalInteractor.isIdleOnCommunal.value) { + if (statusBarStateController.state == StatusBarState.KEYGUARD) { + setHardDozeAmountOverride(dozing = true, source = "Override: communal (keyguard)") + } else { + setHardDozeAmountOverride(dozing = false, source = "Override: communal (shade)") + } + return true + } + return false + } + /** * If the last [setDozeAmount] call was an override to hide notifications, then this call will * check for the set of states that may have caused that override, and if none of them still @@ -483,20 +520,23 @@ constructor( val onKeyguard = statusBarStateController.state == StatusBarState.KEYGUARD val dozing = statusBarStateController.isDozing val bypass = bypassController.bypassEnabled + val idleOnCommunal = communalInteractor.isIdleOnCommunal.value val animating = screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard() - // Overrides are set by [overrideDozeAmountIfAnimatingScreenOff] and - // [overrideDozeAmountIfBypass] based on 'animating' and 'bypass' respectively, so only - // clear the override if both those conditions are cleared. But also require either + // Overrides are set by [overrideDozeAmountIfAnimatingScreenOff], + // [overrideDozeAmountIfBypass] and [overrideDozeAmountIfCommunalShowing] based on + // 'animating', 'bypass' and 'idleOnCommunal' respectively, so only clear the override + // if all of those conditions are cleared. But also require either // !dozing or !onKeyguard because those conditions should indicate that we intend // notifications to be visible, and thus it is safe to unhide them. - val willRemove = (!onKeyguard || !dozing) && !bypass && !animating + val willRemove = (!onKeyguard || !dozing) && !bypass && !animating && !idleOnCommunal logger.logMaybeClearHardDozeAmountOverrideHidingNotifs( willRemove = willRemove, onKeyguard = onKeyguard, dozing = dozing, bypass = bypass, animating = animating, + idleOnCommunal = idleOnCommunal, ) if (willRemove) { clearHardDozeAmountOverride() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt index 502e1d9ea639..9619bea5efd4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt @@ -95,6 +95,7 @@ constructor(@NotificationLockscreenLog private val buffer: LogBuffer) { onKeyguard: Boolean, dozing: Boolean, bypass: Boolean, + idleOnCommunal: Boolean, animating: Boolean, ) { buffer.log( @@ -103,7 +104,7 @@ constructor(@NotificationLockscreenLog private val buffer: LogBuffer) { { str1 = "willRemove=$willRemove onKeyguard=$onKeyguard dozing=$dozing" + - " bypass=$bypass animating=$animating" + " bypass=$bypass animating=$animating idleOnCommunal=$idleOnCommunal" }, { "maybeClearHardDozeAmountOverrideHidingNotifs() $str1" } ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 725c15333b04..9f576066cc98 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -589,8 +589,13 @@ constructor( combine( isOnLockscreen, keyguardInteractor.statusBarState, - ) { isOnLockscreen, statusBarState -> - statusBarState == SHADE_LOCKED || !isOnLockscreen + merge( + primaryBouncerToGoneTransitionViewModel.showAllNotifications, + alternateBouncerToGoneTransitionViewModel.showAllNotifications, + ) + .onStart { emit(false) } + ) { isOnLockscreen, statusBarState, showAllNotifications -> + statusBarState == SHADE_LOCKED || !isOnLockscreen || showAllNotifications } return combineTransform( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index b6f653fabdc4..2798dbfc62e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -164,6 +164,7 @@ import com.android.systemui.qs.QSFragmentLegacy; import com.android.systemui.qs.QSPanelController; import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor; +import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.scrim.ScrimView; import com.android.systemui.settings.UserTracker; @@ -1256,11 +1257,13 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mScreenOffAnimationController.initialize(this, mShadeSurface, mLightRevealScrim); updateLightRevealScrimVisibility(); - mShadeSurface.initDependencies( - this, - mGestureRec, - mShadeController::makeExpandedInvisible, - mHeadsUpManager); + if (!SceneContainerFlag.isEnabled()) { + mShadeSurface.initDependencies( + this, + mGestureRec, + mShadeController::makeExpandedInvisible, + mHeadsUpManager); + } // Set up the quick settings tile panel final View container = getNotificationShadeWindowView().findViewById(R.id.qs_frame); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index febe5a25d3aa..afd2415ad7a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -579,7 +579,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null) { viewRoot.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( - OnBackInvokedDispatcher.PRIORITY_OVERLAY, mOnBackInvokedCallback); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback); mIsBackCallbackRegistered = true; } else { if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 617e1076fca3..c52132f66813 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -46,7 +46,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; -import com.android.systemui.shade.ShadeViewController; +import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameView; import com.android.systemui.statusbar.OperatorNameViewController; @@ -79,6 +79,8 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener; import com.android.systemui.util.settings.SecureSettings; +import kotlin.Unit; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -89,8 +91,6 @@ import java.util.concurrent.Executor; import javax.inject.Inject; -import kotlin.Unit; - import kotlinx.coroutines.DisposableHandle; /** @@ -115,7 +115,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private PhoneStatusBarView mStatusBar; private final StatusBarStateController mStatusBarStateController; private final KeyguardStateController mKeyguardStateController; - private final ShadeViewController mShadeViewController; + private final PanelExpansionInteractor mPanelExpansionInteractor; private MultiSourceMinAlphaController mEndSideAlphaController; private LinearLayout mEndSideContent; private View mClockView; @@ -227,7 +227,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue CollapsedStatusBarViewBinder collapsedStatusBarViewBinder, StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager, KeyguardStateController keyguardStateController, - ShadeViewController shadeViewController, + PanelExpansionInteractor panelExpansionInteractor, StatusBarStateController statusBarStateController, NotificationIconContainerStatusBarViewBinder nicViewBinder, CommandQueue commandQueue, @@ -252,7 +252,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager; mDarkIconManagerFactory = darkIconManagerFactory; mKeyguardStateController = keyguardStateController; - mShadeViewController = shadeViewController; + mPanelExpansionInteractor = panelExpansionInteractor; mStatusBarStateController = statusBarStateController; mNicViewBinder = nicViewBinder; mCommandQueue = commandQueue; @@ -603,7 +603,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private boolean shouldHideStatusBar() { if (!mShadeExpansionStateManager.isClosed() - && mShadeViewController.shouldHideStatusBarIconsWhenExpanded()) { + && mPanelExpansionInteractor.shouldHideStatusBarIconsWhenExpanded()) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractor.kt index c6c88230586f..684e38e25acd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractor.kt @@ -23,6 +23,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlo import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map /** @@ -40,7 +41,7 @@ constructor( private val mobileConnectionsRepository: MobileConnectionsRepository, ) { /** True if the device is currently in airplane mode. */ - val isAirplaneMode: Flow<Boolean> = airplaneModeRepository.isAirplaneMode + val isAirplaneMode: StateFlow<Boolean> = airplaneModeRepository.isAirplaneMode /** True if we're configured to force-hide the airplane mode icon and false otherwise. */ val isForceHidden: Flow<Boolean> = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java index 068e0a6056f3..860068c137a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java @@ -94,7 +94,8 @@ public class SensitiveNotificationProtectionControllerImpl int packageUid; try { - packageUid = mPackageManager.getPackageUid(info.getPackageName(), 0); + packageUid = mPackageManager.getPackageUidAsUser(info.getPackageName(), + info.getUserHandle().getIdentifier()); } catch (PackageManager.NameNotFoundException e) { Log.w(LOG_TAG, "Package " + info.getPackageName() + " not found"); packageUid = -1; diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserActionsUtil.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserActionsUtil.kt index dc004f3603a0..d66fe891589a 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserActionsUtil.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserActionsUtil.kt @@ -24,50 +24,53 @@ import com.android.systemui.user.data.repository.UserRepository /** Utilities related to user management actions. */ object UserActionsUtil { - /** Returns `true` if it's possible to add a guest user to the device; `false` otherwise. */ + /** + * Returns `true` if it's possible for the given user to add a guest user to the device; `false` + * otherwise. + */ fun canCreateGuest( manager: UserManager, repository: UserRepository, isUserSwitcherEnabled: Boolean, - isAddUsersFromLockScreenEnabled: Boolean, + canAddUsersWhenLockedOrDeviceUnlocked: Boolean, ): Boolean { - if (!isUserSwitcherEnabled) { - return false - } - - return currentUserCanCreateUsers(manager, repository) || - anyoneCanCreateUsers(manager, isAddUsersFromLockScreenEnabled) + return canAddMoreUsers( + manager, + repository, + isUserSwitcherEnabled, + canAddUsersWhenLockedOrDeviceUnlocked, + UserManager.USER_TYPE_FULL_GUEST + ) } - /** Returns `true` if it's possible to add a user to the device; `false` otherwise. */ + /** + * Returns `true` if it's possible for the given user to add a user to the device; `false` + * otherwise. + */ fun canCreateUser( manager: UserManager, repository: UserRepository, isUserSwitcherEnabled: Boolean, - isAddUsersFromLockScreenEnabled: Boolean, + canAddUsersWhenLockedOrDeviceUnlocked: Boolean, ): Boolean { - if (!isUserSwitcherEnabled) { - return false - } - - if ( - !currentUserCanCreateUsers(manager, repository) && - !anyoneCanCreateUsers(manager, isAddUsersFromLockScreenEnabled) - ) { - return false - } - - return manager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY) + return canAddMoreUsers( + manager, + repository, + isUserSwitcherEnabled, + canAddUsersWhenLockedOrDeviceUnlocked, + UserManager.USER_TYPE_FULL_SECONDARY + ) } /** - * Returns `true` if it's possible to add a supervised user to the device; `false` otherwise. + * Returns `true` if it's possible to add a supervised user to the device given the current + * user; false` otherwise. */ fun canCreateSupervisedUser( manager: UserManager, repository: UserRepository, isUserSwitcherEnabled: Boolean, - isAddUsersFromLockScreenEnabled: Boolean, + canAddUsersWhenLockedOrDeviceUnlocked: Boolean, supervisedUserPackageName: String? ): Boolean { if (supervisedUserPackageName.isNullOrEmpty()) { @@ -78,17 +81,30 @@ object UserActionsUtil { manager, repository, isUserSwitcherEnabled, - isAddUsersFromLockScreenEnabled + canAddUsersWhenLockedOrDeviceUnlocked ) } - fun canManageUsers( + fun canManageUsers(repository: UserRepository, isUserSwitcherEnabled: Boolean): Boolean { + return isUserSwitcherEnabled && repository.getSelectedUserInfo().isAdmin + } + + /** + * Returns `true` if it's possible to add a user to the device for the given user type; false + * otherwise. + */ + private fun canAddMoreUsers( + manager: UserManager, repository: UserRepository, isUserSwitcherEnabled: Boolean, - isAddUsersFromLockScreenEnabled: Boolean, + canAddUsersWhenLockedOrDeviceUnlocked: Boolean, + userType: String ): Boolean { - return isUserSwitcherEnabled && - (repository.getSelectedUserInfo().isAdmin || isAddUsersFromLockScreenEnabled) + if (!isUserSwitcherEnabled || !canAddUsersWhenLockedOrDeviceUnlocked) { + return false + } + + return currentUserCanCreateUsers(manager, repository) && manager.canAddMoreUsers(userType) } /** @@ -96,28 +112,15 @@ object UserActionsUtil { */ private fun currentUserCanCreateUsers( manager: UserManager, - repository: UserRepository, + repository: UserRepository ): Boolean { val currentUser = repository.getSelectedUserInfo() if (!currentUser.isAdmin && currentUser.id != UserHandle.USER_SYSTEM) { return false } - - return systemCanCreateUsers(manager) - } - - /** Returns `true` if the system can add users to the device; `false` otherwise. */ - private fun systemCanCreateUsers( - manager: UserManager, - ): Boolean { - return !manager.hasBaseUserRestriction(UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM) - } - - /** Returns `true` if it's allowed to add users to the device at all; `false` otherwise. */ - private fun anyoneCanCreateUsers( - manager: UserManager, - isAddUsersFromLockScreenEnabled: Boolean, - ): Boolean { - return systemCanCreateUsers(manager) && isAddUsersFromLockScreenEnabled + return !manager.hasUserRestrictionForUser( + UserManager.DISALLOW_ADD_USER, + UserHandle.of(currentUser.id) + ) && !manager.hasUserRestrictionForUser(UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM) } } diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt index a122311e3b34..382bc03daca8 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt @@ -170,7 +170,8 @@ constructor( keyguardInteractor.isKeyguardShowing, ) { _, userInfos, settings, isDeviceLocked -> buildList { - if (!isDeviceLocked || settings.isAddUsersFromLockscreen) { + val canAccessUserSwitcher = !isDeviceLocked || settings.isAddUsersFromLockscreen + if (canAccessUserSwitcher) { // The device is locked and our setting to allow actions that add users // from the lock-screen is not enabled. We can finish building the list // here. @@ -194,7 +195,10 @@ constructor( when (it) { UserActionModel.ENTER_GUEST_MODE -> { val hasGuestUser = userInfos.any { it.isGuest } - if (!hasGuestUser && canCreateGuestUser(settings)) { + if ( + !hasGuestUser && + canCreateGuestUser(settings, canAccessUserSwitcher) + ) { add(UserActionModel.ENTER_GUEST_MODE) } } @@ -204,7 +208,7 @@ constructor( manager, repository, settings.isUserSwitcherEnabled, - settings.isAddUsersFromLockscreen, + canAccessUserSwitcher ) if (canCreateUsers) { @@ -217,7 +221,7 @@ constructor( manager, repository, settings.isUserSwitcherEnabled, - settings.isAddUsersFromLockscreen, + canAccessUserSwitcher, supervisedUserPackageName, ) ) { @@ -229,11 +233,7 @@ constructor( } } if ( - UserActionsUtil.canManageUsers( - repository, - settings.isUserSwitcherEnabled, - settings.isAddUsersFromLockscreen, - ) + UserActionsUtil.canManageUsers(repository, settings.isUserSwitcherEnabled) ) { add(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) } @@ -820,13 +820,16 @@ constructor( ) } - private fun canCreateGuestUser(settings: UserSwitcherSettingsModel): Boolean { + private fun canCreateGuestUser( + settings: UserSwitcherSettingsModel, + canAccessUserSwitcher: Boolean + ): Boolean { return guestUserInteractor.isGuestUserAutoCreated || UserActionsUtil.canCreateGuest( manager, repository, settings.isUserSwitcherEnabled, - settings.isAddUsersFromLockscreen, + canAccessUserSwitcher, ) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt index 0207d6e8e8c2..04d7b1fa6532 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt @@ -36,10 +36,15 @@ constructor( } fun onSettingsClicked() { - activityStarter.startActivity( - Intent(Settings.ACTION_SOUND_SETTINGS) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), - true, - ) { volumePanelViewModel.dismissPanel() } + activityStarter.startActivityDismissingKeyguard( + /* intent = */ Intent(Settings.ACTION_SOUND_SETTINGS), + /* onlyProvisioned = */ false, + /* dismissShade = */ true, + /* disallowEnterPictureInPictureWhileLaunching = */ false, + /* callback = */ { volumePanelViewModel.dismissPanel() }, + /* flags = */ Intent.FLAG_ACTIVITY_REORDER_TO_FRONT, + /* animationController = */ null, + /* userHandle = */ null, + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt index 3242c2814bc5..57b5d570fbbd 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt @@ -98,7 +98,7 @@ constructor( } } - private fun AudioStreamModel.toState( + private suspend fun AudioStreamModel.toState( isEnabled: Boolean, ringerMode: RingerMode, ): State { @@ -116,7 +116,7 @@ constructor( isEnabled = isEnabled, a11yStep = volumeRange.step, audioStreamModel = this, - isMutable = audioVolumeInteractor.isMutable(audioStream), + isMutable = audioVolumeInteractor.isAffectedByMute(audioStream), ) } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java index 1d9b90ac67af..ff1841819dea 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java @@ -91,17 +91,22 @@ public class QuickAccessWalletController { @Background Executor bgExecutor, SecureSettings secureSettings, QuickAccessWalletClient quickAccessWalletClient, - SystemClock clock) { + SystemClock clock, + RoleManager roleManager) { mContext = context; mExecutor = executor; mBgExecutor = bgExecutor; mSecureSettings = secureSettings; - mRoleManager = mContext.getSystemService(RoleManager.class); + mRoleManager = roleManager; mQuickAccessWalletClient = quickAccessWalletClient; mClock = clock; mQawClientCreatedTimeMillis = mClock.elapsedRealtime(); } + public boolean isWalletRoleAvailable() { + return mRoleManager.isRoleAvailable(RoleManager.ROLE_WALLET); + } + /** * Returns true if the Quick Access Wallet service & feature is available. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java index de696f4b9a2e..71f6081edc23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java @@ -165,7 +165,7 @@ public class MenuViewLayerTest extends SysuiTestCase { mMenuView = spy( new MenuView(mSpyContext, mMenuViewModel, menuViewAppearance, mSecureSettings)); // Ensure tests don't actually update metrics. - doNothing().when(mMenuView).incrementTexMetric(any(), anyInt()); + doNothing().when(mMenuView).incrementTexMetric(any()); mMenuViewLayer = spy(new MenuViewLayer(mSpyContext, mStubWindowManager, mStubAccessibilityManager, mMenuViewModel, menuViewAppearance, mMenuView, @@ -414,33 +414,25 @@ public class MenuViewLayerTest extends SysuiTestCase { @Test @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT) public void onDismissAction_incrementsTexMetricDismiss() { - int uid1 = 1234, uid2 = 5678; mMenuViewModel.onTargetFeaturesChanged( - List.of(new TestAccessibilityTarget(mSpyContext, uid1), - new TestAccessibilityTarget(mSpyContext, uid2))); + List.of(new TestAccessibilityTarget(mSpyContext, 1234), + new TestAccessibilityTarget(mSpyContext, 5678))); mMenuViewLayer.dispatchAccessibilityAction(R.id.action_remove_menu); - ArgumentCaptor<Integer> uidCaptor = ArgumentCaptor.forClass(Integer.class); - verify(mMenuView, times(2)).incrementTexMetric(eq(MenuViewLayer.TEX_METRIC_DISMISS), - uidCaptor.capture()); - assertThat(uidCaptor.getAllValues()).containsExactly(uid1, uid2); + verify(mMenuView, times(1)).incrementTexMetric(eq(MenuViewLayer.TEX_METRIC_DISMISS)); } @Test @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT) public void onEditAction_incrementsTexMetricEdit() { - int uid1 = 1234, uid2 = 5678; mMenuViewModel.onTargetFeaturesChanged( - List.of(new TestAccessibilityTarget(mSpyContext, uid1), - new TestAccessibilityTarget(mSpyContext, uid2))); + List.of(new TestAccessibilityTarget(mSpyContext, 1234), + new TestAccessibilityTarget(mSpyContext, 5678))); mMenuViewLayer.dispatchAccessibilityAction(R.id.action_edit); - ArgumentCaptor<Integer> uidCaptor = ArgumentCaptor.forClass(Integer.class); - verify(mMenuView, times(2)).incrementTexMetric(eq(MenuViewLayer.TEX_METRIC_EDIT), - uidCaptor.capture()); - assertThat(uidCaptor.getAllValues()).containsExactly(uid1, uid2); + verify(mMenuView, times(1)).incrementTexMetric(eq(MenuViewLayer.TEX_METRIC_EDIT)); } /** Simplified AccessibilityTarget for testing MenuViewLayer. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt index 58011eb63f69..190babdb22b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt @@ -27,7 +27,7 @@ class BackAnimationSpecTest : SysuiTestCase() { val maxY = 14.0f val minScale = 0.9f - val backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(displayMetrics) + val backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi { displayMetrics } assertBackTransformation( backAnimationSpec = backAnimationSpec, diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt index f5c9befef0ef..314abda66401 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt @@ -30,7 +30,7 @@ class OnBackAnimationCallbackExtensionTest : SysuiTestCase() { private val onBackAnimationCallback = onBackAnimationCallbackFrom( - backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(displayMetrics), + backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi { displayMetrics }, displayMetrics = displayMetrics, onBackProgressed = onBackProgress, onBackStarted = onBackStart, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/repository/IconTilesRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/repository/IconTilesRepositoryImplTest.kt new file mode 100644 index 000000000000..8cc3a85ef6c8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/repository/IconTilesRepositoryImplTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 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.panels.domain.repository + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.qs.panels.data.repository.IconTilesRepositoryImpl +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class IconTilesRepositoryImplTest : SysuiTestCase() { + + private val underTest = IconTilesRepositoryImpl() + + @Test + fun iconTilesSpecsIsValid() = runTest { + val tilesSpecs by collectLastValue(underTest.iconTilesSpecs) + assertThat(tilesSpecs).isEqualTo(ICON_ONLY_TILES_SPECS) + } + + companion object { + private val ICON_ONLY_TILES_SPECS = + setOf( + TileSpec.create("airplane"), + TileSpec.create("battery"), + TileSpec.create("cameratoggle"), + TileSpec.create("cast"), + TileSpec.create("color_correction"), + TileSpec.create("inversion"), + TileSpec.create("saver"), + TileSpec.create("dnd"), + TileSpec.create("flashlight"), + TileSpec.create("location"), + TileSpec.create("mictoggle"), + TileSpec.create("nfc"), + TileSpec.create("night"), + TileSpec.create("rotation") + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index 2d18f9293ec8..122d9e414d13 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -58,7 +58,6 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; @@ -68,6 +67,7 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.wallet.controller.QuickAccessWalletController; @@ -198,7 +198,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test - public void testIsAvailable_qawFeatureAvailable() { + public void testIsAvailable_qawFeatureAvailableWalletUnavailable() { + when(mController.isWalletRoleAvailable()).thenReturn(false); when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(true); when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false); when(mSecureSettings.getStringForUser(NFC_PAYMENT_DEFAULT_COMPONENT, @@ -208,6 +209,16 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test + public void testIsAvailable_nfcUnavailableWalletAvailable() { + when(mController.isWalletRoleAvailable()).thenReturn(true); + when(mHost.getUserId()).thenReturn(PRIMARY_USER_ID); + when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(false); + when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false); + + assertTrue(mTile.isAvailable()); + } + + @Test public void testHandleClick_startQuickAccessUiIntent_noCard() { setUpWalletCard(/* hasCard= */ false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index 07d93508228e..5ca6cf12ba90 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -41,6 +41,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.sceneDataSourceDelegator import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.phone.SystemUIDialogFactory import com.android.systemui.testKosmos @@ -104,7 +105,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { dialogFactory, keyguardTransitionInteractor, shadeInteractor, - powerManager + powerManager, + kosmos.sceneDataSourceDelegator, ) testableLooper = TestableLooper.get(this) @@ -145,6 +147,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { keyguardTransitionInteractor, shadeInteractor, powerManager, + kosmos.sceneDataSourceDelegator, ) // First call succeeds. @@ -268,7 +271,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } private fun goToScene(scene: SceneKey) { - communalRepository.setDesiredScene(scene) + communalRepository.changeScene(scene) testableLooper.processAllMessages() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index e957ca2eca3a..d534974d0551 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -604,6 +604,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { new NotificationsKeyguardInteractor(notifsKeyguardViewStateRepository); NotificationWakeUpCoordinator coordinator = new NotificationWakeUpCoordinator( + mKosmos.getTestScope().getBackgroundScope(), mDumpManager, mock(HeadsUpManager.class), new StatusBarStateControllerImpl( @@ -618,7 +619,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mDozeParameters, mScreenOffAnimationController, new NotificationWakeUpCoordinatorLogger(logcatLogBuffer()), - notifsKeyguardInteractor); + notifsKeyguardInteractor, + mKosmos.getCommunalInteractor()); mConfigurationController = new ConfigurationControllerImpl(mContext); PulseExpansionHandler expansionHandler = new PulseExpansionHandler( mContext, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 29a92d969ca4..650c45bf83ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -1061,7 +1061,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo @Test public void testPanelClosedWhenClosingQsInSplitShade() { mShadeExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 1, - /* expanded= */ true, /* tracking= */ false, /* dragDownPxAmount= */ 0); + /* expanded= */ true, /* tracking= */ false); enableSplitShade(/* enabled= */ true); mNotificationPanelViewController.setExpandedFraction(1f); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index b6996131ea09..7a3b561e2f8f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -46,6 +46,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.res.R import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler +import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.statusbar.DragDownHelper import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.NotificationInsetsController @@ -68,6 +69,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow @@ -85,7 +87,6 @@ import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -import java.util.Optional import org.mockito.Mockito.`when` as whenever @OptIn(ExperimentalCoroutinesApi::class) @@ -100,7 +101,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock private lateinit var dozeServiceHost: DozeServiceHost @Mock private lateinit var dozeScrimController: DozeScrimController @Mock private lateinit var dockManager: DockManager - @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController + @Mock private lateinit var shadeViewController: ShadeViewController + @Mock private lateinit var panelExpansionInteractor: PanelExpansionInteractor @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController @@ -177,7 +179,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { dockManager, notificationShadeDepthController, view, - notificationPanelViewController, + shadeViewController, + panelExpansionInteractor, ShadeExpansionStateManager(), stackScrollLayoutController, statusBarKeyguardViewManager, @@ -263,7 +266,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { testScope.runTest { underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) - whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true) + whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) .thenReturn(true) whenever(phoneStatusBarViewController.sendTouchToView(DOWN_EVENT)).thenReturn(true) @@ -282,7 +285,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) .thenReturn(true) // Item we're testing - whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(false) + whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(false) val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -295,7 +298,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { testScope.runTest { underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) - whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true) + whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) // Item we're testing whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) .thenReturn(false) @@ -310,7 +313,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() = testScope.runTest { underTest.setStatusBarViewController(phoneStatusBarViewController) - whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true) + whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) .thenReturn(true) // Item we're testing @@ -327,7 +330,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { testScope.runTest { underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) - whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true) + whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) .thenReturn(true) @@ -486,7 +489,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { // AND bouncer is not showing whenever(centralSurfaces.isBouncerShowing()).thenReturn(false) // AND panel view controller wants it - whenever(notificationPanelViewController.handleExternalInterceptTouch(DOWN_EVENT)) + whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT)) .thenReturn(true) mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index 2ecca2e5c4ca..0f54e0718233 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.res.R import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler +import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.statusbar.DragDownHelper import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.NotificationInsetsController @@ -89,7 +90,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var dozeServiceHost: DozeServiceHost @Mock private lateinit var dozeScrimController: DozeScrimController @Mock private lateinit var dockManager: DockManager - @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController + @Mock private lateinit var shadeViewController: ShadeViewController + @Mock private lateinit var panelExpansionInteractor: PanelExpansionInteractor @Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController @@ -166,7 +168,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { dockManager, notificationShadeDepthController, underTest, - notificationPanelViewController, + shadeViewController, + panelExpansionInteractor, ShadeExpansionStateManager(), notificationStackScrollLayoutController, statusBarKeyguardViewManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeExpansionStateManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeExpansionStateManagerTest.kt index 15c04eb2e2bc..89ae42fd9f83 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeExpansionStateManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeExpansionStateManagerTest.kt @@ -42,17 +42,11 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { val tracking = true val dragDownAmount = 1234f - shadeExpansionStateManager.onPanelExpansionChanged( - fraction, - expanded, - tracking, - dragDownAmount - ) + shadeExpansionStateManager.onPanelExpansionChanged(fraction, expanded, tracking) assertThat(listener.fraction).isEqualTo(fraction) assertThat(listener.expanded).isEqualTo(expanded) assertThat(listener.tracking).isEqualTo(tracking) - assertThat(listener.dragDownAmountPx).isEqualTo(dragDownAmount) } @Test @@ -61,12 +55,7 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { val expanded = true val tracking = true val dragDownAmount = 1234f - shadeExpansionStateManager.onPanelExpansionChanged( - fraction, - expanded, - tracking, - dragDownAmount - ) + shadeExpansionStateManager.onPanelExpansionChanged(fraction, expanded, tracking) val listener = TestShadeExpansionListener() val currentState = shadeExpansionStateManager.addExpansionListener(listener) @@ -75,7 +64,6 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { assertThat(listener.fraction).isEqualTo(fraction) assertThat(listener.expanded).isEqualTo(expanded) assertThat(listener.tracking).isEqualTo(tracking) - assertThat(listener.dragDownAmountPx).isEqualTo(dragDownAmount) } @Test @@ -100,8 +88,7 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { shadeExpansionStateManager.onPanelExpansionChanged( fraction = 0.5f, expanded = true, - tracking = false, - dragDownPxAmount = 0f + tracking = false ) assertThat(listener.state).isEqualTo(STATE_OPENING) @@ -115,8 +102,7 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { shadeExpansionStateManager.onPanelExpansionChanged( fraction = 0.5f, expanded = true, - tracking = true, - dragDownPxAmount = 0f + tracking = true ) assertThat(listener.state).isEqualTo(STATE_OPENING) @@ -132,8 +118,7 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { shadeExpansionStateManager.onPanelExpansionChanged( fraction = 0.5f, expanded = false, - tracking = false, - dragDownPxAmount = 0f + tracking = false ) assertThat(listener.state).isEqualTo(STATE_CLOSED) @@ -149,8 +134,7 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { shadeExpansionStateManager.onPanelExpansionChanged( fraction = 0.5f, expanded = false, - tracking = true, - dragDownPxAmount = 0f + tracking = true ) assertThat(listener.state).isEqualTo(STATE_OPEN) @@ -166,8 +150,7 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { shadeExpansionStateManager.onPanelExpansionChanged( fraction = 1f, expanded = true, - tracking = false, - dragDownPxAmount = 0f + tracking = false ) assertThat(listener.previousState).isEqualTo(STATE_OPENING) @@ -182,8 +165,7 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { shadeExpansionStateManager.onPanelExpansionChanged( fraction = 1f, expanded = true, - tracking = true, - dragDownPxAmount = 0f + tracking = true ) assertThat(listener.state).isEqualTo(STATE_OPENING) @@ -199,8 +181,7 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { shadeExpansionStateManager.onPanelExpansionChanged( fraction = 1f, expanded = false, - tracking = false, - dragDownPxAmount = 0f + tracking = false ) assertThat(listener.state).isEqualTo(STATE_CLOSED) @@ -216,8 +197,7 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { shadeExpansionStateManager.onPanelExpansionChanged( fraction = 1f, expanded = false, - tracking = true, - dragDownPxAmount = 0f + tracking = true ) assertThat(listener.state).isEqualTo(STATE_OPEN) @@ -229,13 +209,11 @@ class ShadeExpansionStateManagerTest : SysuiTestCase() { var fraction: Float = 0f var expanded: Boolean = false var tracking: Boolean = false - var dragDownAmountPx: Float = 0f override fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) { this.fraction = event.fraction this.expanded = event.expanded this.tracking = event.tracking - this.dragDownAmountPx = event.dragDownPxAmount } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt index dea905aad132..f2abb909e004 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt @@ -3,23 +3,18 @@ package com.android.systemui.shade.transition import android.platform.test.annotations.DisableFlags import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.compose.animation.scene.ObservableTransitionState -import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.dump.DumpManager -import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.FakeSceneDataSource -import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionStateManager @@ -27,15 +22,8 @@ import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.shade.domain.interactor.panelExpansionInteractor import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.whenever -import com.google.common.truth.Truth import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -74,9 +62,7 @@ class ScrimShadeTransitionControllerTest : SysuiTestCase() { sceneInteractor = kosmos.sceneInteractor fakeSceneDataSource = kosmos.fakeSceneDataSource underTest = ScrimShadeTransitionController( - applicationScope, shadeExpansionStateManager, - { panelExpansionInteractor }, dumpManager, scrimController, ) @@ -99,96 +85,20 @@ class ScrimShadeTransitionControllerTest : SysuiTestCase() { verify(scrimController).setRawPanelExpansionFraction(DEFAULT_EXPANSION_EVENT.fraction) } - @Test - @EnableSceneContainer - fun sceneChanges_forwardsToScrimTransitionController() = - testScope.runTest { - var rawExpansion: Float? = null - whenever(scrimController.setRawPanelExpansionFraction(any())).then { - (it.arguments[0] as Float?).also { rawExp -> rawExpansion = rawExp } - } - setUnlocked(true) - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(Scenes.Gone) - ) - sceneInteractor.setTransitionState(transitionState) - - changeScene(Scenes.Gone, transitionState) - val currentScene by collectLastValue(sceneInteractor.currentScene) - Truth.assertThat(currentScene).isEqualTo(Scenes.Gone) - - Truth.assertThat(rawExpansion) - .isEqualTo(0f) - - changeScene(Scenes.Shade, transitionState) { progress -> - Truth.assertThat(rawExpansion) - .isEqualTo(progress) - } - } - private fun startLegacyPanelExpansion() { shadeExpansionStateManager.onPanelExpansionChanged( DEFAULT_EXPANSION_EVENT.fraction, DEFAULT_EXPANSION_EVENT.expanded, DEFAULT_EXPANSION_EVENT.tracking, - DEFAULT_EXPANSION_EVENT.dragDownPxAmount, ) } - private fun TestScope.setUnlocked(isUnlocked: Boolean) { - val isDeviceUnlocked by collectLastValue(deviceUnlockedInteractor.isDeviceUnlocked) - deviceEntryRepository.setUnlocked(isUnlocked) - runCurrent() - - Truth.assertThat(isDeviceUnlocked).isEqualTo(isUnlocked) - } - - private fun TestScope.changeScene( - toScene: SceneKey, - transitionState: MutableStateFlow<ObservableTransitionState>, - assertDuringProgress: ((progress: Float) -> Unit) = {}, - ) { - val currentScene by collectLastValue(sceneInteractor.currentScene) - val progressFlow = MutableStateFlow(0f) - transitionState.value = - ObservableTransitionState.Transition( - fromScene = checkNotNull(currentScene), - toScene = toScene, - progress = progressFlow, - isInitiatedByUserInput = true, - isUserInputOngoing = flowOf(true), - ) - runCurrent() - assertDuringProgress(progressFlow.value) - - progressFlow.value = 0.2f - runCurrent() - assertDuringProgress(progressFlow.value) - - progressFlow.value = 0.6f - runCurrent() - assertDuringProgress(progressFlow.value) - - progressFlow.value = 1f - runCurrent() - assertDuringProgress(progressFlow.value) - - transitionState.value = ObservableTransitionState.Idle(toScene) - fakeSceneDataSource.changeScene(toScene) - runCurrent() - assertDuringProgress(progressFlow.value) - - Truth.assertThat(currentScene).isEqualTo(toScene) - } - companion object { val DEFAULT_EXPANSION_EVENT = ShadeExpansionChangeEvent( fraction = 0.5f, expanded = true, - tracking = true, - dragDownPxAmount = 10f + tracking = true ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index 68bc72b09f94..fc0c85e30d5a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -23,18 +23,18 @@ import android.view.Choreographer import android.view.View import android.view.ViewRootImpl import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.policy.FakeConfigurationController -import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.util.WallpaperController import com.android.systemui.util.mockito.eq import com.google.common.truth.Truth.assertThat @@ -142,7 +142,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun onPanelExpansionChanged_apliesBlur_ifShade() { notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f)) + fraction = 1f, expanded = true, tracking = false)) verify(shadeAnimation).animateTo(eq(maxBlur)) } @@ -150,7 +150,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun onPanelExpansionChanged_animatesBlurIn_ifShade() { notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 0.01f, expanded = false, tracking = false, dragDownPxAmount = 0f)) + fraction = 0.01f, expanded = false, tracking = false)) verify(shadeAnimation).animateTo(eq(maxBlur)) } @@ -160,7 +160,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { clearInvocations(shadeAnimation) notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 0f, expanded = false, tracking = false, dragDownPxAmount = 0f)) + fraction = 0f, expanded = false, tracking = false)) verify(shadeAnimation).animateTo(eq(0)) } @@ -168,7 +168,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun onPanelExpansionChanged_animatesBlurOut_ifFlick() { val event = ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f) + fraction = 1f, expanded = true, tracking = false) onPanelExpansionChanged_apliesBlur_ifShade() clearInvocations(shadeAnimation) notificationShadeDepthController.onPanelExpansionChanged(event) @@ -189,7 +189,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { clearInvocations(shadeAnimation) notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 0.6f, expanded = true, tracking = true, dragDownPxAmount = 0f)) + fraction = 0.6f, expanded = true, tracking = true)) verify(shadeAnimation).animateTo(eq(maxBlur)) } @@ -197,7 +197,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun onPanelExpansionChanged_respectsMinPanelPullDownFraction() { val event = ShadeExpansionChangeEvent( - fraction = 0.5f, expanded = true, tracking = true, dragDownPxAmount = 0f) + fraction = 0.5f, expanded = true, tracking = true) notificationShadeDepthController.panelPullDownMinFraction = 0.5f notificationShadeDepthController.onPanelExpansionChanged(event) assertThat(notificationShadeDepthController.shadeExpansion).isEqualTo(0f) @@ -225,7 +225,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { notificationShadeDepthController.qsPanelExpansion = 1f notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f)) + fraction = 1f, expanded = true, tracking = false)) notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false)) } @@ -236,7 +236,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { notificationShadeDepthController.qsPanelExpansion = 0.25f notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f)) + fraction = 1f, expanded = true, tracking = false)) notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(wallpaperController) .setNotificationShadeZoom(eq(ShadeInterpolation.getNotificationScrimAlpha(0.25f))) @@ -248,7 +248,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f)) + fraction = 1f, expanded = true, tracking = false)) notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(wallpaperController).setNotificationShadeZoom(0f) @@ -260,7 +260,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f)) + fraction = 1f, expanded = true, tracking = false)) notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(wallpaperController).setNotificationShadeZoom(floatThat { it > 0 }) @@ -273,7 +273,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { val expanded = true val tracking = false val dragDownPxAmount = 0f - val event = ShadeExpansionChangeEvent(rawFraction, expanded, tracking, dragDownPxAmount) + val event = ShadeExpansionChangeEvent(rawFraction, expanded, tracking) val inOrder = Mockito.inOrder(wallpaperController) notificationShadeDepthController.onPanelExpansionChanged(event) @@ -338,7 +338,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun updateBlurCallback_setsBlur_whenExpanded() { notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f)) + fraction = 1f, expanded = true, tracking = false)) `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat()) notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false)) @@ -348,7 +348,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun updateBlurCallback_ignoreShadeBlurUntilHidden_overridesZoom() { notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f)) + fraction = 1f, expanded = true, tracking = false)) `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat()) notificationShadeDepthController.blursDisabledForAppLaunch = true notificationShadeDepthController.updateBlurCallback.doFrame(0) @@ -367,7 +367,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun ignoreBlurForUnlock_ignores() { notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f)) + fraction = 1f, expanded = true, tracking = false)) `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat()) notificationShadeDepthController.blursDisabledForAppLaunch = false @@ -384,7 +384,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun ignoreBlurForUnlock_doesNotIgnore() { notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f)) + fraction = 1f, expanded = true, tracking = false)) `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat()) notificationShadeDepthController.blursDisabledForAppLaunch = false @@ -416,7 +416,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { // And shade is blurred notificationShadeDepthController.onPanelExpansionChanged( ShadeExpansionChangeEvent( - fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f)) + fraction = 1f, expanded = true, tracking = false)) `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat()) notificationShadeDepthController.updateBlurCallback.doFrame(0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt new file mode 100644 index 000000000000..9c59f9bf49d8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar + +import android.telephony.ServiceState +import android.telephony.SubscriptionInfo +import android.telephony.TelephonyManager +import android.telephony.telephonyManager +import androidx.test.filters.SmallTest +import com.android.keyguard.keyguardUpdateMonitor +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.plugins.DarkIconDispatcher +import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor +import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository +import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy +import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository +import com.android.systemui.tuner.TunerService +import com.android.systemui.util.CarrierConfigTracker +import com.android.systemui.util.kotlin.JavaAdapter +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import junit.framework.Assert.assertTrue +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +class OperatorNameViewControllerTest : SysuiTestCase() { + private lateinit var underTest: OperatorNameViewController + private lateinit var airplaneModeInteractor: AirplaneModeInteractor + + private val kosmos = Kosmos() + private val testScope = TestScope() + + private val view = OperatorNameView(mContext) + private val javaAdapter = JavaAdapter(testScope.backgroundScope) + + @Mock private lateinit var darkIconDispatcher: DarkIconDispatcher + @Mock private lateinit var tunerService: TunerService + private var telephonyManager = kosmos.telephonyManager + private val keyguardUpdateMonitor = kosmos.keyguardUpdateMonitor + @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker + private val subscriptionManagerProxy = FakeSubscriptionManagerProxy() + + private val airplaneModeRepository = FakeAirplaneModeRepository() + private val connectivityRepository = FakeConnectivityRepository() + private val mobileConnectionsRepository = FakeMobileConnectionsRepository() + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + airplaneModeInteractor = + AirplaneModeInteractor( + airplaneModeRepository, + connectivityRepository, + mobileConnectionsRepository, + ) + + underTest = + OperatorNameViewController.Factory( + darkIconDispatcher, + tunerService, + telephonyManager, + keyguardUpdateMonitor, + carrierConfigTracker, + airplaneModeInteractor, + subscriptionManagerProxy, + javaAdapter, + ) + .create(view) + } + + @Test + fun updateFromSubInfo_showsCarrieName() = + testScope.runTest { + whenever(telephonyManager.isDataCapable).thenReturn(true) + + val mockSubInfo = + mock<SubscriptionInfo>().also { + whenever(it.subscriptionId).thenReturn(1) + whenever(it.carrierName).thenReturn("test_carrier") + } + whenever(keyguardUpdateMonitor.getSubscriptionInfoForSubId(any())) + .thenReturn(mockSubInfo) + whenever(keyguardUpdateMonitor.getSimState(any())) + .thenReturn(TelephonyManager.SIM_STATE_READY) + whenever(keyguardUpdateMonitor.getServiceState(any())) + .thenReturn(ServiceState().also { it.state = ServiceState.STATE_IN_SERVICE }) + subscriptionManagerProxy.defaultDataSubId = 1 + airplaneModeRepository.setIsAirplaneMode(false) + + underTest.onViewAttached() + runCurrent() + + assertThat(view.text).isEqualTo("test_carrier") + } + + @Test + fun notDataCapable_doesNotShowOperatorName() = + testScope.runTest { + whenever(telephonyManager.isDataCapable).thenReturn(false) + + val mockSubInfo = + mock<SubscriptionInfo>().also { + whenever(it.subscriptionId).thenReturn(1) + whenever(it.carrierName).thenReturn("test_carrier") + } + whenever(keyguardUpdateMonitor.getSubscriptionInfoForSubId(any())) + .thenReturn(mockSubInfo) + whenever(keyguardUpdateMonitor.getSimState(any())) + .thenReturn(TelephonyManager.SIM_STATE_READY) + whenever(keyguardUpdateMonitor.getServiceState(any())) + .thenReturn(ServiceState().also { it.state = ServiceState.STATE_IN_SERVICE }) + subscriptionManagerProxy.defaultDataSubId = 1 + airplaneModeRepository.setIsAirplaneMode(false) + + underTest.onViewAttached() + runCurrent() + + assertTrue(view.text.isNullOrEmpty()) + } + + @Test + fun airplaneMode_doesNotShowOperatorName() = + testScope.runTest { + whenever(telephonyManager.isDataCapable).thenReturn(false) + val mockSubInfo = + mock<SubscriptionInfo>().also { + whenever(it.subscriptionId).thenReturn(1) + whenever(it.carrierName).thenReturn("test_carrier") + } + whenever(keyguardUpdateMonitor.getSubscriptionInfoForSubId(any())) + .thenReturn(mockSubInfo) + whenever(keyguardUpdateMonitor.getSimState(any())) + .thenReturn(TelephonyManager.SIM_STATE_READY) + whenever(keyguardUpdateMonitor.getServiceState(any())) + .thenReturn(ServiceState().also { it.state = ServiceState.STATE_IN_SERVICE }) + subscriptionManagerProxy.defaultDataSubId = 1 + airplaneModeRepository.setIsAirplaneMode(true) + + underTest.onViewAttached() + runCurrent() + + assertTrue(view.text.isNullOrEmpty()) + } + + @Test + fun notInService_doesNotShowOperatorName() = + testScope.runTest { + // Data capable + whenever(telephonyManager.isDataCapable).thenReturn(true) + + // Valid subscription + val mockSubInfo = + mock<SubscriptionInfo>().also { + whenever(it.subscriptionId).thenReturn(1) + whenever(it.carrierName).thenReturn("test_carrier") + } + whenever(keyguardUpdateMonitor.getSubscriptionInfoForSubId(any())) + .thenReturn(mockSubInfo) + whenever(keyguardUpdateMonitor.getSimState(any())) + .thenReturn(TelephonyManager.SIM_STATE_READY) + + // Not in service + whenever(keyguardUpdateMonitor.getServiceState(any())) + .thenReturn(ServiceState().also { it.state = ServiceState.STATE_OUT_OF_SERVICE }) + // Subscription is default for data + subscriptionManagerProxy.defaultDataSubId = 1 + // Not airplane mode + airplaneModeRepository.setIsAirplaneMode(false) + + underTest.onViewAttached() + runCurrent() + + assertTrue(view.text.isNullOrEmpty()) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt index 82093adcb75c..67b540cd762e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt @@ -19,10 +19,15 @@ package com.android.systemui.statusbar.notification import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.animation.AnimatorTestRule +import com.android.systemui.communal.data.repository.communalRepository +import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dump.DumpManager -import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeViewController.Companion.WAKEUP_ANIMATION_DELAY_MS @@ -34,11 +39,16 @@ import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.statusbar.policy.HeadsUpManager +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test @@ -49,6 +59,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions +@OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidTestingRunner::class) @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @@ -56,7 +67,8 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() { @get:Rule val animatorTestRule = AnimatorTestRule(this) - private val kosmos = Kosmos() + private val kosmos = testKosmos() + private val testScope = kosmos.testScope private val dumpManager: DumpManager = mock() private val headsUpManager: HeadsUpManager = mock() @@ -97,6 +109,7 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() { whenever(statusBarStateController.state).then { statusBarState } notificationWakeUpCoordinator = NotificationWakeUpCoordinator( + kosmos.applicationCoroutineScope, dumpManager, headsUpManager, statusBarStateController, @@ -105,6 +118,7 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() { screenOffAnimationController, logger, kosmos.notificationsKeyguardInteractor, + kosmos.communalInteractor, ) statusBarStateCallback = withArgCaptor { verify(statusBarStateController).addCallback(capture()) @@ -161,6 +175,39 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() { } @Test + fun setDozeToZeroWhenCommunalShowingWillFullyHideNotifications() = + testScope.runTest { + val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) + ) + kosmos.communalRepository.setTransitionState(transitionState) + runCurrent() + setDozeAmount(0f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + } + + @Test + fun closingCommunalWillShowNotifications() = + testScope.runTest { + val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(CommunalScenes.Communal) + ) + kosmos.communalRepository.setTransitionState(transitionState) + runCurrent() + setDozeAmount(0f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + + transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Blank) + runCurrent() + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0f, hideAmount = 0f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + } + + @Test fun switchingToShadeWithBypassEnabledWillShowNotifications() { setDozeToZeroWithBypassWillFullyHideNotifications() clearInvocations(stackScrollerController) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 0e89d8072a2e..06a4d0820386 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -59,7 +59,8 @@ import com.android.internal.R; import com.android.internal.widget.CachingIconView; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestableContext; -import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.flags.FakeFeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.AboveShelfChangedListener; @@ -90,13 +91,14 @@ import java.util.function.Consumer; @RunWithLooper public class ExpandableNotificationRowTest extends SysuiTestCase { - private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); + private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic(); private NotificationTestHelper mNotificationTestHelper; @Rule public MockitoRule mockito = MockitoJUnit.rule(); @Before public void setUp() throws Exception { allowTestableLooperAsMainThread(); + mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false); mNotificationTestHelper = new NotificationTestHelper( mContext, mDependency, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 054680df1582..34605fed7d28 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -597,8 +597,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { private static ShadeExpansionChangeEvent expansionEvent( float fraction, boolean expanded, boolean tracking) { - return new ShadeExpansionChangeEvent( - fraction, expanded, tracking, /* dragDownPxAmount= */ 0f); + return new ShadeExpansionChangeEvent(fraction, expanded, tracking); } @Test @@ -607,7 +606,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { /* verify that a predictive back callback is registered when the bouncer becomes visible */ mBouncerExpansionCallback.onVisibilityChanged(true); verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( - eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY), + eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), mBackCallbackCaptor.capture()); /* verify that the same callback is unregistered when the bouncer becomes invisible */ @@ -622,7 +621,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mBouncerExpansionCallback.onVisibilityChanged(true); /* capture the predictive back callback during registration */ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( - eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY), + eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), mBackCallbackCaptor.capture()); when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true); @@ -642,7 +641,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mBouncerExpansionCallback.onVisibilityChanged(true); /* capture the predictive back callback during registration */ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( - eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY), + eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), mBackCallbackCaptor.capture()); assertTrue(mBackCallbackCaptor.getValue() instanceof OnBackAnimationCallback); @@ -660,7 +659,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mBouncerExpansionCallback.onVisibilityChanged(true); /* capture the predictive back callback during registration */ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( - eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY), + eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), mBackCallbackCaptor.capture()); assertTrue(mBackCallbackCaptor.getValue() instanceof OnBackAnimationCallback); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 3da5ab9f9d3d..9c3d9c669adc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -52,7 +52,7 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; -import com.android.systemui.shade.ShadeViewController; +import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; @@ -115,7 +115,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Mock private HeadsUpAppearanceController mHeadsUpAppearanceController; @Mock - private ShadeViewController mShadeViewController; + private PanelExpansionInteractor mPanelExpansionInteractor; @Mock private StatusBarIconController.DarkIconManager.Factory mIconManagerFactory; @Mock @@ -304,7 +304,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // WHEN the shade is open and configured to hide the status bar icons mShadeExpansionStateManager.updateState(STATE_OPEN); - when(mShadeViewController.shouldHideStatusBarIconsWhenExpanded()).thenReturn(true); + when(mPanelExpansionInteractor.shouldHideStatusBarIconsWhenExpanded()).thenReturn(true); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); @@ -320,7 +320,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // WHEN the shade is open but *not* configured to hide the status bar icons mShadeExpansionStateManager.updateState(STATE_OPEN); - when(mShadeViewController.shouldHideStatusBarIconsWhenExpanded()).thenReturn(false); + when(mPanelExpansionInteractor.shouldHideStatusBarIconsWhenExpanded()).thenReturn(false); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); @@ -337,7 +337,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // WHEN the shade is open and configured to hide the status bar icons mShadeExpansionStateManager.updateState(STATE_OPEN); - when(mShadeViewController.shouldHideStatusBarIconsWhenExpanded()).thenReturn(true); + when(mPanelExpansionInteractor.shouldHideStatusBarIconsWhenExpanded()).thenReturn(true); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); @@ -696,7 +696,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mCollapsedStatusBarViewBinder, mStatusBarHideIconsForBouncerManager, mKeyguardStateController, - mShadeViewController, + mPanelExpansionInteractor, mStatusBarStateController, mock(NotificationIconContainerStatusBarViewBinder.class), mCommandQueue, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt index 867476f9023c..581ca3b14822 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt @@ -28,6 +28,7 @@ import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE import android.content.pm.PackageManager import android.media.projection.MediaProjectionInfo import android.media.projection.MediaProjectionManager +import android.os.UserHandle import android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.RequiresFlagsDisabled @@ -85,7 +86,6 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { @Mock private lateinit var activityManager: IActivityManager @Mock private lateinit var mediaProjectionManager: MediaProjectionManager @Mock private lateinit var packageManager: PackageManager - @Mock private lateinit var mediaProjectionInfo: MediaProjectionInfo @Mock private lateinit var listener1: Runnable @Mock private lateinit var listener2: Runnable @Mock private lateinit var listener3: Runnable @@ -95,6 +95,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { private lateinit var globalSettings: FakeGlobalSettings private lateinit var mediaProjectionCallback: MediaProjectionManager.Callback private lateinit var controller: SensitiveNotificationProtectionControllerImpl + private lateinit var mediaProjectionInfo: MediaProjectionInfo @Before fun setUp() { @@ -109,14 +110,29 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { setShareFullScreen() whenever(activityManager.bugreportWhitelistedPackages) .thenReturn(listOf(BUGREPORT_PACKAGE_NAME)) - whenever(packageManager.getPackageUid(TEST_PROJECTION_PACKAGE_NAME, 0)) + whenever( + packageManager.getPackageUidAsUser( + TEST_PROJECTION_PACKAGE_NAME, + UserHandle.CURRENT.identifier + ) + ) .thenReturn(TEST_PROJECTION_PACKAGE_UID) - whenever(packageManager.getPackageUid(BUGREPORT_PACKAGE_NAME, 0)) + whenever( + packageManager.getPackageUidAsUser( + BUGREPORT_PACKAGE_NAME, + UserHandle.CURRENT.identifier + ) + ) .thenReturn(BUGREPORT_PACKAGE_UID) // SystemUi context package name is exempt, but in test scenarios its // com.android.systemui.tests so use that instead of hardcoding. Setup packagemanager to // return the correct uid in this scenario - whenever(packageManager.getPackageUid(mContext.packageName, 0)) + whenever( + packageManager.getPackageUidAsUser( + mContext.packageName, + UserHandle.CURRENT.identifier + ) + ) .thenReturn(mContext.applicationInfo.uid) whenever(packageManager.checkPermission(anyString(), anyString())) @@ -271,7 +287,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { fun isSensitiveStateActive_projectionActive_sysuiExempt_false() { // SystemUi context package name is exempt, but in test scenarios its // com.android.systemui.tests so use that instead of hardcoding - whenever(mediaProjectionInfo.packageName).thenReturn(mContext.packageName) + setShareFullScreenViaSystemUi() mediaProjectionCallback.onStart(mediaProjectionInfo) assertFalse(controller.isSensitiveStateActive) @@ -309,7 +325,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { @Test fun isSensitiveStateActive_projectionActive_bugReportHandlerExempt_false() { - whenever(mediaProjectionInfo.packageName).thenReturn(BUGREPORT_PACKAGE_NAME) + setShareFullScreenViaBugReportHandler() mediaProjectionCallback.onStart(mediaProjectionInfo) assertFalse(controller.isSensitiveStateActive) @@ -371,7 +387,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { fun shouldProtectNotification_projectionActive_sysuiExempt_false() { // SystemUi context package name is exempt, but in test scenarios its // com.android.systemui.tests so use that instead of hardcoding - whenever(mediaProjectionInfo.packageName).thenReturn(mContext.packageName) + setShareFullScreenViaSystemUi() mediaProjectionCallback.onStart(mediaProjectionInfo) val notificationEntry = setupNotificationEntry(TEST_PACKAGE_NAME, false) @@ -415,7 +431,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { @Test fun shouldProtectNotification_projectionActive_bugReportHandlerExempt_false() { - whenever(mediaProjectionInfo.packageName).thenReturn(BUGREPORT_PACKAGE_NAME) + setShareFullScreenViaBugReportHandler() mediaProjectionCallback.onStart(mediaProjectionInfo) val notificationEntry = setupNotificationEntry(TEST_PACKAGE_NAME, false) @@ -548,9 +564,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { fun logSensitiveContentProtectionSession_exemptViaSystemUi() { // SystemUi context package name is exempt, but in test scenarios its // com.android.systemui.tests so use that instead of hardcoding - val testPackageName = mContext.packageName - val testUid = mContext.applicationInfo.uid - whenever(mediaProjectionInfo.packageName).thenReturn(testPackageName) + setShareFullScreenViaSystemUi() mediaProjectionCallback.onStart(mediaProjectionInfo) @@ -558,7 +572,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { FrameworkStatsLog.write( eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION), anyLong(), - eq(testUid), + eq(mContext.applicationInfo.uid), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) @@ -571,7 +585,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { FrameworkStatsLog.write( eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION), anyLong(), - eq(testUid), + eq(mContext.applicationInfo.uid), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) @@ -582,8 +596,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { @Test fun logSensitiveContentProtectionSession_exemptViaBugReportHandler() { // Setup exempt via bugreport handler - whenever(mediaProjectionInfo.packageName).thenReturn(BUGREPORT_PACKAGE_NAME) - + setShareFullScreenViaBugReportHandler() mediaProjectionCallback.onStart(mediaProjectionInfo) verify { @@ -619,13 +632,26 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { } private fun setShareFullScreen() { - whenever(mediaProjectionInfo.packageName).thenReturn(TEST_PROJECTION_PACKAGE_NAME) - whenever(mediaProjectionInfo.launchCookie).thenReturn(null) + setShareScreen(TEST_PROJECTION_PACKAGE_NAME, true) + } + + private fun setShareFullScreenViaBugReportHandler() { + setShareScreen(BUGREPORT_PACKAGE_NAME, true) + } + + private fun setShareFullScreenViaSystemUi() { + // SystemUi context package name is exempt, but in test scenarios its + // com.android.systemui.tests so use that instead of hardcoding + setShareScreen(mContext.packageName, true) } private fun setShareSingleApp() { - whenever(mediaProjectionInfo.packageName).thenReturn(TEST_PROJECTION_PACKAGE_NAME) - whenever(mediaProjectionInfo.launchCookie).thenReturn(ActivityOptions.LaunchCookie()) + setShareScreen(TEST_PROJECTION_PACKAGE_NAME, false) + } + + private fun setShareScreen(packageName: String, fullScreen: Boolean) { + val launchCookie = if (fullScreen) null else ActivityOptions.LaunchCookie() + mediaProjectionInfo = MediaProjectionInfo(packageName, UserHandle.CURRENT, launchCookie) } private fun setupNotificationEntry( diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt index 1b4385148f88..3dee093bd594 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt @@ -1014,6 +1014,136 @@ class UserSwitcherInteractorTest : SysuiTestCase() { verify(spyContext, never()).startServiceAsUser(any(), any()) } + @Test + fun userIsAdminAndRestricted_addUserActionsNotAdded() { + createUserInteractor() + testScope.runTest { + val id = 0 + val userInfo = + UserInfo( + id, + "child", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_ADMIN, + UserManager.USER_TYPE_FULL_RESTRICTED, + ) + whenever( + manager.hasUserRestrictionForUser( + UserManager.DISALLOW_ADD_USER, + UserHandle.of(id) + ) + ) + .thenReturn(true) + + userRepository.setUserInfos(listOf(userInfo)) + userRepository.setSelectedUserInfo(userInfo) + userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) + + val value = collectLastValue(underTest.actions) + runCurrent() + + assertThat(value()).isEqualTo(listOf(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)) + } + } + + @Test + fun userIsNotRestrictedAndCannotAddGuests_actionsDoesNotIncludeAddGuest() { + createUserInteractor() + testScope.runTest { + val userInfos = createUserInfos(count = 2, includeGuest = false) + + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[0]) + userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) + keyguardRepository.setKeyguardShowing(false) + + whenever(manager.canAddMoreUsers(UserManager.USER_TYPE_FULL_GUEST)).thenReturn(false) + + val value = collectLastValue(underTest.actions) + runCurrent() + + assertThat(value()) + .isEqualTo( + listOf( + UserActionModel.ADD_USER, + UserActionModel.ADD_SUPERVISED_USER, + UserActionModel.NAVIGATE_TO_USER_MANAGEMENT, + ) + ) + } + } + + @Test + fun userIsNotRestrictedAndCannotAddUsers_actionsDoesNotIncludeAddUsers() { + createUserInteractor() + testScope.runTest { + val userInfos = createUserInfos(count = 2, includeGuest = false) + + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[0]) + userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) + keyguardRepository.setKeyguardShowing(false) + + whenever(manager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY)) + .thenReturn(false) + + val value = collectLastValue(underTest.actions) + runCurrent() + + assertThat(value()) + .isEqualTo( + listOf( + UserActionModel.ENTER_GUEST_MODE, + UserActionModel.NAVIGATE_TO_USER_MANAGEMENT, + ) + ) + } + } + + @Test + fun systemUserHasRestrictions_addUserActionsNotAdded() { + createUserInteractor() + testScope.runTest { + val systemId = 0 + val systemUser = + UserInfo( + systemId, + "system", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_SYSTEM, + UserManager.USER_TYPE_SYSTEM_HEADLESS, + ) + val adminId = 10 + val adminUser = + UserInfo( + adminId, + "admin", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_ADMIN, + UserManager.USER_TYPE_FULL_SYSTEM, + ) + + userRepository.setUserInfos(listOf(systemUser, adminUser)) + userRepository.setSelectedUserInfo(adminUser) + userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) + keyguardRepository.setKeyguardShowing(false) + + whenever(headlessSystemUserMode.isHeadlessSystemUserMode()).thenReturn(true) + whenever( + manager.hasUserRestrictionForUser( + UserManager.DISALLOW_ADD_USER, + UserHandle.of(0) + ) + ) + .thenReturn(true) + + val value = collectLastValue(underTest.actions) + runCurrent() + + assertThat(value()).isEqualTo(listOf(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)) + } + } + private fun assertUsers( models: List<UserModel>?, count: Int, diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java index fccb936a3bc8..dc5597a1cce0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.PendingIntent; +import android.app.role.RoleManager; import android.content.Intent; import android.service.quickaccesswallet.GetWalletCardsRequest; import android.service.quickaccesswallet.QuickAccessWalletClient; @@ -54,11 +55,14 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.List; + @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest public class QuickAccessWalletControllerTest extends SysuiTestCase { + private static final String WALLET_ROLE_HOLDER = "wallet.role.holder"; @Mock private QuickAccessWalletClient mQuickAccessWalletClient; @Mock @@ -69,6 +73,8 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase { private ActivityStarter mActivityStarter; @Mock private ActivityTransitionAnimator.Controller mAnimationController; + @Mock + private RoleManager mRoleManager; @Captor private ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor; @Captor @@ -102,7 +108,8 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase { MoreExecutors.directExecutor(), mSecureSettings, mQuickAccessWalletClient, - mClock); + mClock, + mRoleManager); } @Test @@ -113,6 +120,24 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase { } @Test + public void walletRoleAvailable_isAvailable() { + when(mRoleManager.isRoleAvailable(eq(RoleManager.ROLE_WALLET))).thenReturn(true); + when(mRoleManager.getRoleHolders(eq(RoleManager.ROLE_WALLET))) + .thenReturn(List.of(WALLET_ROLE_HOLDER)); + + assertTrue(mController.isWalletRoleAvailable()); + } + + @Test + public void walletRoleAvailable_isNotAvailable() { + when(mRoleManager.isRoleAvailable(eq(RoleManager.ROLE_WALLET))).thenReturn(false); + when(mRoleManager.getRoleHolders(eq(RoleManager.ROLE_WALLET))) + .thenReturn(List.of(WALLET_ROLE_HOLDER)); + + assertFalse(mController.isWalletRoleAvailable()); + } + + @Test public void walletServiceUnavailable_walletNotEnabled() { when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt index bc0bf9dd069f..de7b14d1e102 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt @@ -32,6 +32,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInt import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor import com.android.systemui.scene.SceneContainerFrameworkModule import com.android.systemui.scene.shared.flag.SceneContainerFlags +import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneDataSource import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import com.android.systemui.shade.domain.interactor.BaseShadeInteractor @@ -45,6 +46,7 @@ import dagger.Provides import javax.inject.Provider import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -71,7 +73,10 @@ interface SysUITestModule { @Binds @Main fun bindMainResources(resources: Resources): Resources @Binds fun bindBroadcastDispatcher(fake: FakeBroadcastDispatcher): BroadcastDispatcher @Binds @SysUISingleton fun bindsShadeInteractor(sii: ShadeInteractorImpl): ShadeInteractor - @Binds fun bindSceneDataSource(delegator: SceneDataSourceDelegator): SceneDataSource + + @Binds + @SysUISingleton + fun bindSceneDataSource(delegator: SceneDataSourceDelegator): SceneDataSource @Binds fun provideFaceAuthInteractor( @@ -109,6 +114,15 @@ interface SysUITestModule { sceneContainerOff.get() } } + + @Provides + @SysUISingleton + fun providesSceneDataSourceDelegator( + @Application applicationScope: CoroutineScope, + config: SceneContainerConfig, + ): SceneDataSourceDelegator { + return SceneDataSourceDelegator(applicationScope, config) + } } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt index 5ff588f810bd..9f5c6b8faa38 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt @@ -2,6 +2,7 @@ package com.android.systemui.communal.data.repository import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import com.android.systemui.communal.shared.model.CommunalScenes import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -17,11 +18,11 @@ import kotlinx.coroutines.flow.stateIn @OptIn(ExperimentalCoroutinesApi::class) class FakeCommunalRepository( applicationScope: CoroutineScope, - override val desiredScene: MutableStateFlow<SceneKey> = + override val currentScene: MutableStateFlow<SceneKey> = MutableStateFlow(CommunalScenes.Default), ) : CommunalRepository { - override fun setDesiredScene(desiredScene: SceneKey) { - this.desiredScene.value = desiredScene + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) { + this.currentScene.value = toScene } private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt index f0fedd2ed479..1e25f7fd470e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt @@ -20,6 +20,7 @@ import com.android.systemui.biometrics.authController import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.shade.domain.interactor.shadeInteractor val Kosmos.lockscreenContentViewModel by @@ -30,5 +31,6 @@ val Kosmos.lockscreenContentViewModel by authController = authController, longPress = keyguardLongPressViewModel, shadeInteractor = shadeInteractor, + applicationScope = applicationCoroutineScope, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt index 513c6ab3586d..4a2eaf0f7bf6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt @@ -27,6 +27,7 @@ import com.android.systemui.kosmos.testDispatcher import com.android.systemui.log.LogBuffer import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationShadeWindowController @@ -78,4 +79,11 @@ val Kosmos.shadeControllerImpl by { mock<NotificationGutsManager>() }, ) } -var Kosmos.shadeController: ShadeController by Kosmos.Fixture { shadeControllerImpl } +var Kosmos.shadeController: ShadeController by + Kosmos.Fixture { + if (SceneContainerFlag.isEnabled) { + shadeControllerSceneImpl + } else { + shadeControllerImpl + } + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt index 7dfa68605ef3..b85858d915b5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt @@ -22,11 +22,17 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.log.LogBuffer +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.shade.data.repository.shadeRepository +import com.android.systemui.shade.domain.interactor.panelExpansionInteractor import com.android.systemui.shade.transition.ScrimShadeTransitionController import com.android.systemui.statusbar.policy.splitShadeStateController import com.android.systemui.util.mockito.mock +@Deprecated("ShadeExpansionStateManager is deprecated. Remove your dependency on it instead.") +val Kosmos.shadeExpansionStateManager by Fixture { ShadeExpansionStateManager() } + val Kosmos.shadeStartable by Fixture { ShadeStartable( applicationScope = applicationCoroutineScope, @@ -34,7 +40,10 @@ val Kosmos.shadeStartable by Fixture { touchLog = mock<LogBuffer>(), configurationRepository = configurationRepository, shadeRepository = shadeRepository, - controller = splitShadeStateController, + splitShadeStateController = splitShadeStateController, scrimShadeTransitionController = mock<ScrimShadeTransitionController>(), + sceneInteractorProvider = { sceneInteractor }, + panelExpansionInteractorProvider = { panelExpansionInteractor }, + shadeExpansionStateManager = shadeExpansionStateManager, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt index a3ad2b87d5f5..4788624bdf02 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt @@ -44,6 +44,8 @@ class FakeAudioRepository : AudioRepository { private val models: MutableMap<AudioStream, MutableStateFlow<AudioStreamModel>> = mutableMapOf() private val lastAudibleVolumes: MutableMap<AudioStream, Int> = mutableMapOf() + private var isAffectedByMute: MutableMap<AudioStream, Boolean> = mutableMapOf() + private fun getAudioStreamModelState( audioStream: AudioStream ): MutableStateFlow<AudioStreamModel> = @@ -93,4 +95,15 @@ class FakeAudioRepository : AudioRepository { fun setLastAudibleVolume(audioStream: AudioStream, volume: Int) { lastAudibleVolumes[audioStream] = volume } + + override suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) { + mutableRingerMode.value = mode + } + + override suspend fun isAffectedByMute(audioStream: AudioStream): Boolean = + isAffectedByMute[audioStream] ?: true + + fun setIsAffectedByMute(audioStream: AudioStream, isAffected: Boolean) { + isAffectedByMute[audioStream] = isAffected + } } diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index 178102e2876d..8905ad3273d8 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -151,6 +151,18 @@ java_device_for_host { filegroup { name: "ravenwood-services-jarjar-rules", - srcs: ["ravenwood-services-jarjar-rules.txt"], + srcs: ["texts/ravenwood-services-jarjar-rules.txt"], visibility: ["//frameworks/base"], } + +// For collecting the *stats.csv files in a known directory under out/host/linux-x86/testcases/. +// The "test" just shows the available stats filenames. +sh_test_host { + name: "ravenwood-stats-checker", + src: "scripts/ravenwood-stats-checker.sh", + test_suites: ["general-tests"], + data: [ + ":framework-minus-apex.ravenwood.stats", + ":services.core.ravenwood.stats", + ], +} diff --git a/ravenwood/bulk_enable.py b/ravenwood/scripts/bulk_enable.py index aafaaff7561a..aafaaff7561a 100644 --- a/ravenwood/bulk_enable.py +++ b/ravenwood/scripts/bulk_enable.py diff --git a/ravenwood/fix_test_runner.py b/ravenwood/scripts/fix_test_runner.py index 99b7a1ff226b..99b7a1ff226b 100755 --- a/ravenwood/fix_test_runner.py +++ b/ravenwood/scripts/fix_test_runner.py diff --git a/ravenwood/list-ravenwood-tests.sh b/ravenwood/scripts/list-ravenwood-tests.sh index fb9b823ee93b..fb9b823ee93b 100755 --- a/ravenwood/list-ravenwood-tests.sh +++ b/ravenwood/scripts/list-ravenwood-tests.sh diff --git a/ravenwood/scripts/ravenwood-stats-checker.sh b/ravenwood/scripts/ravenwood-stats-checker.sh new file mode 100755 index 000000000000..93f4a3fe4333 --- /dev/null +++ b/ravenwood/scripts/ravenwood-stats-checker.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Copyright (C) 2024 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. + +# Just print the available *.csv filenames. +echo '#Stats files:' +ls *.csv
\ No newline at end of file diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh new file mode 100755 index 000000000000..4dcaa2be5af3 --- /dev/null +++ b/ravenwood/scripts/ravenwood-stats-collector.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Copyright (C) 2024 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. + +# Script to collect the ravenwood "stats" CVS files and create a single file. + +set -e + +# Output file +out=/tmp/ravenwood-stats-all.csv + +# Where the input files are. +path=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/ravenwood-stats-checker/x86_64/ + +m() { + ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@" +} + +# Building this will generate the files we need. +m ravenwood-stats-checker + +# Start... + +cd $path + +dump() { + local jar=$1 + local file=$2 + + sed -e '1d' -e "s/^/$jar,/" $file +} + +collect() { + echo 'Jar,PackageName,ClassName,SupportedMethods,TotalMethods' + dump "framework-minus-apex" hoststubgen_framework-minus-apex_stats.csv + dump "service.core" hoststubgen_services.core_stats.csv +} + +collect >$out + +echo "Full dump CVS created at $out" diff --git a/ravenwood/run-ravenwood-tests.sh b/ravenwood/scripts/run-ravenwood-tests.sh index a303626bb445..926c08f4e689 100755 --- a/ravenwood/run-ravenwood-tests.sh +++ b/ravenwood/scripts/run-ravenwood-tests.sh @@ -15,7 +15,7 @@ # Run all the ravenwood tests + hoststubgen unit tests. -all_tests="hoststubgentest tiny-framework-dump-test hoststubgen-invoke-test" +all_tests="hoststubgentest tiny-framework-dump-test hoststubgen-invoke-test ravenwood-stats-checker" # "echo" is to remove the newlines all_tests="$all_tests $(echo $(${0%/*}/list-ravenwood-tests.sh) )" diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/texts/framework-minus-apex-ravenwood-policies.txt index 371c3acab144..371c3acab144 100644 --- a/ravenwood/framework-minus-apex-ravenwood-policies.txt +++ b/ravenwood/texts/framework-minus-apex-ravenwood-policies.txt diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt index 9b4d378cc7b7..9b4d378cc7b7 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt diff --git a/ravenwood/ravenwood-services-jarjar-rules.txt b/ravenwood/texts/ravenwood-services-jarjar-rules.txt index 8fdd3408f74d..8fdd3408f74d 100644 --- a/ravenwood/ravenwood-services-jarjar-rules.txt +++ b/ravenwood/texts/ravenwood-services-jarjar-rules.txt diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/texts/ravenwood-standard-options.txt index f64f26d7962a..f64f26d7962a 100644 --- a/ravenwood/ravenwood-standard-options.txt +++ b/ravenwood/texts/ravenwood-standard-options.txt diff --git a/ravenwood/services.core-ravenwood-policies.txt b/ravenwood/texts/services.core-ravenwood-policies.txt index d8d563e05435..d8d563e05435 100644 --- a/ravenwood/services.core-ravenwood-policies.txt +++ b/ravenwood/texts/services.core-ravenwood-policies.txt diff --git a/services/Android.bp b/services/Android.bp index 7bbb42e9a88f..29d1acf5f350 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -254,6 +254,7 @@ java_library { required: [ "libukey2_jni_shared", "protolog.conf.json.gz", + "core.protolog.pb", ], lint: { baseline_filename: "lint-baseline.xml", diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 2d593094aef7..8ac1eb9c90b7 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -82,7 +82,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.ArraySet; import android.util.ExceptionUtils; -import android.util.Log; import android.util.Slog; import com.android.internal.app.IAppOpsService; @@ -121,8 +120,7 @@ import java.util.Set; @SuppressLint("LongLogTag") public class CompanionDeviceManagerService extends SystemService { - static final String TAG = "CDM_CompanionDeviceManagerService"; - static final boolean DEBUG = false; + private static final String TAG = "CDM_CompanionDeviceManagerService"; private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min @@ -135,7 +133,6 @@ public class CompanionDeviceManagerService extends SystemService { private final IAppOpsService mAppOpsManager; private final PowerExemptionManager mPowerExemptionManager; private final PackageManagerInternal mPackageManagerInternal; - private final PowerManagerInternal mPowerManagerInternal; private final AssociationStore mAssociationStore; private final SystemDataTransferRequestStore mSystemDataTransferRequestStore; @@ -160,7 +157,8 @@ public class CompanionDeviceManagerService extends SystemService { mAmInternal = LocalServices.getService(ActivityManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); final UserManager userManager = context.getSystemService(UserManager.class); - mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); + final PowerManagerInternal powerManagerInternal = LocalServices.getService( + PowerManagerInternal.class); final AssociationDiskStore associationDiskStore = new AssociationDiskStore(); mAssociationStore = new AssociationStore(context, userManager, associationDiskStore); @@ -178,7 +176,7 @@ public class CompanionDeviceManagerService extends SystemService { mDevicePresenceProcessor = new DevicePresenceProcessor(context, mCompanionAppBinder, userManager, mAssociationStore, mObservableUuidStore, - mPowerManagerInternal); + powerManagerInternal); mTransportManager = new CompanionTransportManager(context, mAssociationStore); @@ -252,11 +250,6 @@ public class CompanionDeviceManagerService extends SystemService { private void onPackageRemoveOrDataClearedInternal( @UserIdInt int userId, @NonNull String packageName) { - if (DEBUG) { - Log.i(TAG, "onPackageRemove_Or_DataCleared() u" + userId + "/" - + packageName); - } - // Clear all associations for the package. final List<AssociationInfo> associationsForPackage = mAssociationStore.getAssociationsByPackage(userId, packageName); @@ -279,8 +272,6 @@ public class CompanionDeviceManagerService extends SystemService { } private void onPackageModifiedInternal(@UserIdInt int userId, @NonNull String packageName) { - if (DEBUG) Log.i(TAG, "onPackageModified() u" + userId + "/" + packageName); - final List<AssociationInfo> associationsForPackage = mAssociationStore.getAssociationsByPackage(userId, packageName); for (AssociationInfo association : associationsForPackage) { diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java index 253fe35a3e29..ac19d8bc897f 100644 --- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java +++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java @@ -161,20 +161,20 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } if (DEBUG) Log.d(TAG, "onBootPhase - PHASE_BOOT_COMPLETED"); - - mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); init(getContext().getSystemService(MediaProjectionManager.class), LocalServices.getService(WindowManagerInternal.class), - getExemptedPackages()); + LocalServices.getService(PackageManagerInternal.class), + getExemptedPackages() + ); if (sensitiveContentAppProtection()) { publishBinderService(Context.SENSITIVE_CONTENT_PROTECTION_SERVICE, - new SensitiveContentProtectionManagerServiceBinder(mPackageManagerInternal)); + new SensitiveContentProtectionManagerServiceBinder()); } } @VisibleForTesting void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager, - ArraySet<String> exemptedPackages) { + PackageManagerInternal packageManagerInternal, ArraySet<String> exemptedPackages) { if (DEBUG) Log.d(TAG, "init"); Objects.requireNonNull(projectionManager); @@ -182,6 +182,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic mProjectionManager = projectionManager; mWindowManager = windowManager; + mPackageManagerInternal = packageManagerInternal; mExemptedPackages = exemptedPackages; // TODO(b/317250444): use MediaProjectionManagerService directly, reduces unnecessary @@ -231,14 +232,16 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } private void onProjectionStart(MediaProjectionInfo projectionInfo) { - int uid = mPackageManagerInternal.getPackageUid(projectionInfo.getPackageName(), 0, - projectionInfo.getUserHandle().getIdentifier()); boolean isPackageExempted = (mExemptedPackages != null && mExemptedPackages.contains( projectionInfo.getPackageName())) - || canRecordSensitiveContent(projectionInfo.getPackageName()); + || canRecordSensitiveContent(projectionInfo.getPackageName()) + || isAutofillServiceRecorderPackage(projectionInfo.getUserHandle().getIdentifier(), + projectionInfo.getPackageName()); // TODO(b/324447419): move GlobalSettings lookup to background thread boolean isFeatureDisabled = Settings.Global.getInt(getContext().getContentResolver(), DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0) != 0; + int uid = mPackageManagerInternal.getPackageUid(projectionInfo.getPackageName(), 0, + projectionInfo.getUserHandle().getIdentifier()); mMediaProjectionSession = new MediaProjectionSession( uid, isPackageExempted || isFeatureDisabled, new Random().nextLong()); @@ -295,8 +298,9 @@ public final class SensitiveContentProtectionManagerService extends SystemServic // notify windowmanager of any currently posted sensitive content notifications ArraySet<PackageInfo> packageInfos = getSensitivePackagesFromNotifications(notifications, rankingMap); - - mWindowManager.addBlockScreenCaptureForApps(packageInfos); + if (packageInfos.size() > 0) { + mWindowManager.addBlockScreenCaptureForApps(packageInfos); + } } private ArraySet<PackageInfo> getSensitivePackagesFromNotifications( @@ -422,6 +426,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic if (!mProjectionActive) { return; } + if (DEBUG) { Log.d(TAG, "setSensitiveContentProtection - current package=" + packageInfo + ", isShowingSensitiveContent=" + isShowingSensitiveContent @@ -452,15 +457,29 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } } - private final class SensitiveContentProtectionManagerServiceBinder - extends ISensitiveContentProtectionManager.Stub { - private final PackageManagerInternal mPackageManagerInternal; + // TODO: b/328251279 - Autofill service exemption is temporary and will be removed in future. + private boolean isAutofillServiceRecorderPackage(int userId, String projectionPackage) { + String autofillServiceName = Settings.Secure.getStringForUser( + getContext().getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, userId); + if (DEBUG) { + Log.d(TAG, "autofill service for user " + userId + " is " + autofillServiceName); + } - SensitiveContentProtectionManagerServiceBinder( - PackageManagerInternal packageManagerInternal) { - mPackageManagerInternal = packageManagerInternal; + if (autofillServiceName == null) { + return false; + } + ComponentName serviceComponent = ComponentName.unflattenFromString(autofillServiceName); + if (serviceComponent == null) { + return false; } + String autofillServicePackage = serviceComponent.getPackageName(); + return autofillServicePackage != null + && autofillServicePackage.equals(projectionPackage); + } + + private final class SensitiveContentProtectionManagerServiceBinder + extends ISensitiveContentProtectionManager.Stub { public void setSensitiveContentProtection(IBinder windowToken, String packageName, boolean isShowingSensitiveContent) { Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b00676a15c0d..47f03f3f16fa 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -414,6 +414,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener; import com.android.internal.os.BinderCallHeavyHitterWatcher.HeavyHitterContainer; import com.android.internal.os.BinderInternal; +import com.android.internal.os.BinderInternal.BinderProxyCountEventListener; import com.android.internal.os.BinderTransactionNameResolver; import com.android.internal.os.ByteTransferPipe; import com.android.internal.os.IResultReceiver; @@ -614,8 +615,8 @@ public class ActivityManagerService extends IActivityManager.Stub private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB /** - * The number of binder proxies we need to have before we start warning and - * dumping debug info. + * The number of binder proxies we need to have before we start dumping debug info + * and kill the offenders. */ private static final int BINDER_PROXY_HIGH_WATERMARK = 6000; @@ -625,6 +626,11 @@ public class ActivityManagerService extends IActivityManager.Stub */ private static final int BINDER_PROXY_LOW_WATERMARK = 5500; + /** + * The number of binder proxies we need to have before we start warning. + */ + private static final int BINDER_PROXY_WARNING_WATERMARK = 5750; + // Max character limit for a notification title. If the notification title is larger than this // the notification will not be legible to the user. private static final int MAX_BUGREPORT_TITLE_SIZE = 100; @@ -8015,6 +8021,18 @@ public class ActivityManagerService extends IActivityManager.Stub return uidRecord != null && !uidRecord.isSetIdle(); } + @Override + public long getUidLastIdleElapsedTime(int uid, String callingPackage) { + if (!hasUsageStatsPermission(callingPackage)) { + enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, + "getUidLastIdleElapsedTime"); + } + synchronized (mProcLock) { + final UidRecord uidRecord = mProcessList.getUidRecordLOSP(uid); + return uidRecord != null ? uidRecord.getRealLastIdleTime() : 0; + } + } + @GuardedBy("mUidFrozenStateChangedCallbackList") private final RemoteCallbackList<IUidFrozenStateChangedCallback> mUidFrozenStateChangedCallbackList = new RemoteCallbackList<>(); @@ -9051,34 +9069,10 @@ public class ActivityManagerService extends IActivityManager.Stub t.traceBegin("setBinderProxies"); BinderInternal.nSetBinderProxyCountWatermarks(BINDER_PROXY_HIGH_WATERMARK, - BINDER_PROXY_LOW_WATERMARK); + BINDER_PROXY_LOW_WATERMARK, BINDER_PROXY_WARNING_WATERMARK); BinderInternal.nSetBinderProxyCountEnabled(true); - BinderInternal.setBinderProxyCountCallback( - (uid) -> { - Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid " - + Process.myUid()); - BinderProxy.dumpProxyDebugInfo(); - CriticalEventLog.getInstance().logExcessiveBinderCalls(uid); - if (uid == Process.SYSTEM_UID) { - Slog.i(TAG, "Skipping kill (uid is SYSTEM)"); - } else { - killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid), - ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE, - ApplicationExitInfo.SUBREASON_EXCESSIVE_BINDER_OBJECTS, - "Too many Binders sent to SYSTEM"); - // We need to run a GC here, because killing the processes involved - // actually isn't guaranteed to free up the proxies; in fact, if the - // GC doesn't run for a long time, we may even exceed the global - // proxy limit for a process (20000), resulting in system_server itself - // being killed. - // Note that the GC here might not actually clean up all the proxies, - // because the binder reference decrements will come in asynchronously; - // but if new processes belonging to the UID keep adding proxies, we - // will get another callback here, and run the GC again - this time - // cleaning up the old proxies. - VMRuntime.getRuntime().requestConcurrentGC(); - } - }, mHandler); + BinderInternal.setBinderProxyCountCallback(new MyBinderProxyCountEventListener(), + mHandler); t.traceEnd(); // setBinderProxies t.traceEnd(); // ActivityManagerStartApps @@ -9093,6 +9087,46 @@ public class ActivityManagerService extends IActivityManager.Stub } } + private class MyBinderProxyCountEventListener implements BinderProxyCountEventListener { + @Override + public void onLimitReached(int uid) { + Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid " + + Process.myUid()); + BinderProxy.dumpProxyDebugInfo(); + CriticalEventLog.getInstance().logExcessiveBinderCalls(uid); + if (uid == Process.SYSTEM_UID) { + Slog.i(TAG, "Skipping kill (uid is SYSTEM)"); + } else { + killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid), + ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE, + ApplicationExitInfo.SUBREASON_EXCESSIVE_BINDER_OBJECTS, + "Too many Binders sent to SYSTEM"); + // We need to run a GC here, because killing the processes involved + // actually isn't guaranteed to free up the proxies; in fact, if the + // GC doesn't run for a long time, we may even exceed the global + // proxy limit for a process (20000), resulting in system_server itself + // being killed. + // Note that the GC here might not actually clean up all the proxies, + // because the binder reference decrements will come in asynchronously; + // but if new processes belonging to the UID keep adding proxies, we + // will get another callback here, and run the GC again - this time + // cleaning up the old proxies. + VMRuntime.getRuntime().requestConcurrentGC(); + } + } + + @Override + public void onWarningThresholdReached(int uid) { + if (Flags.logExcessiveBinderProxies()) { + Slog.w(TAG, "Uid " + uid + " sent too many (" + + BINDER_PROXY_WARNING_WATERMARK + ") Binders to uid " + Process.myUid()); + FrameworkStatsLog.write( + FrameworkStatsLog.EXCESSIVE_BINDER_PROXY_COUNT_REPORTED, + uid); + } + } + } + private void watchDeviceProvisioning(Context context) { // setting system property based on whether device is provisioned diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index df6481dc2c74..9c1ce666eddf 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -3681,7 +3681,7 @@ public class OomAdjuster { for (int i = N - 1; i >= 0; i--) { final UidRecord uidRec = mActiveUids.valueAt(i); final long bgTime = uidRec.getLastBackgroundTime(); - final long idleTime = uidRec.getLastIdleTime(); + final long idleTime = uidRec.getLastIdleTimeIfStillIdle(); if (bgTime > 0 && (!uidRec.isIdle() || idleTime == 0)) { if (bgTime <= maxBgTime) { EventLogTags.writeAmUidIdle(uidRec.getUid()); diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index 45fd470401e2..86fa0fccf774 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -17,6 +17,7 @@ package com.android.server.am; import android.Manifest; +import android.annotation.ElapsedRealtimeLong; import android.app.ActivityManager; import android.content.pm.PackageManager; import android.os.SystemClock; @@ -65,8 +66,19 @@ public final class UidRecord { @CompositeRWLock({"mService", "mProcLock"}) private long mLastBackgroundTime; + /** + * Last time the UID became idle. This is set to 0, once the UID becomes active. + */ + @ElapsedRealtimeLong @CompositeRWLock({"mService", "mProcLock"}) - private long mLastIdleTime; + private long mLastIdleTimeIfStillIdle; + + /** + * Last time the UID became idle. Unlike {@link #mLastIdleTimeIfStillIdle}, we never clear it. + */ + @ElapsedRealtimeLong + @CompositeRWLock({"mService", "mProcLock"}) + private long mRealLastIdleTime; @CompositeRWLock({"mService", "mProcLock"}) private boolean mEphemeral; @@ -257,14 +269,28 @@ public final class UidRecord { mLastBackgroundTime = lastBackgroundTime; } + /** + * Last time the UID became idle. This is set to 0, once the UID becomes active. + */ + @GuardedBy(anyOf = {"mService", "mProcLock"}) + long getLastIdleTimeIfStillIdle() { + return mLastIdleTimeIfStillIdle; + } + + /** + * Last time the UID became idle. Unlike {@link #getLastIdleTimeIfStillIdle}, we never clear it. + */ @GuardedBy(anyOf = {"mService", "mProcLock"}) - long getLastIdleTime() { - return mLastIdleTime; + long getRealLastIdleTime() { + return mRealLastIdleTime; } @GuardedBy({"mService", "mProcLock"}) - void setLastIdleTime(long lastActiveTime) { - mLastIdleTime = lastActiveTime; + void setLastIdleTime(@ElapsedRealtimeLong long lastIdleTime) { + mLastIdleTimeIfStillIdle = lastIdleTime; + if (lastIdleTime > 0) { + mRealLastIdleTime = lastIdleTime; + } } @GuardedBy(anyOf = {"mService", "mProcLock"}) diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 14f3120b60b6..7df63b1dab66 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -869,9 +869,7 @@ public class AuthService extends SystemService { } if (faceAidlInstances != null && faceAidlInstances.length > 0) { - mFaceSensorConfigurations.addAidlConfigs(faceAidlInstances, - name -> IFace.Stub.asInterface(Binder.allowBlocking( - ServiceManager.waitForDeclaredService(name)))); + mFaceSensorConfigurations.addAidlConfigs(faceAidlInstances); } if (faceService != null) { @@ -909,9 +907,7 @@ public class AuthService extends SystemService { } if (fingerprintAidlInstances != null && fingerprintAidlInstances.length > 0) { - mFingerprintSensorConfigurations.addAidlSensors(fingerprintAidlInstances, - name -> IFingerprint.Stub.asInterface(Binder.allowBlocking( - ServiceManager.waitForDeclaredService(name)))); + mFingerprintSensorConfigurations.addAidlSensors(fingerprintAidlInstances); } if (fingerprintService != null) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 7ee2a7ababb3..1037124d7048 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -729,8 +729,8 @@ public class FaceService extends SystemService { private List<ServiceProvider> getProviders( FaceSensorConfigurations faceSensorConfigurations) { final List<ServiceProvider> providers = new ArrayList<>(); - final Pair<String, SensorProps[]> filteredSensorProps = - filterAvailableHalInstances(faceSensorConfigurations); + final Pair<String, SensorProps[]> filteredSensorProps = filterAvailableHalInstances( + faceSensorConfigurations); providers.add(mFaceProviderFunction.getFaceProvider(filteredSensorProps, faceSensorConfigurations.getResetLockoutRequiresChallenge())); return providers; @@ -739,28 +739,36 @@ public class FaceService extends SystemService { @NonNull private Pair<String, SensorProps[]> filterAvailableHalInstances( FaceSensorConfigurations faceSensorConfigurations) { - Pair<String, SensorProps[]> finalSensorPair = faceSensorConfigurations.getSensorPair(); + String finalSensorInstance = faceSensorConfigurations.getSensorInstance(); if (faceSensorConfigurations.isSingleSensorConfigurationPresent()) { - return finalSensorPair; + return new Pair<>(finalSensorInstance, + faceSensorConfigurations.getSensorPropForInstance(finalSensorInstance)); } - - final Pair<String, SensorProps[]> virtualSensorProps = faceSensorConfigurations - .getSensorPairForInstance("virtual"); - - if (Utils.isVirtualEnabled(getContext())) { - if (virtualSensorProps != null) { - return virtualSensorProps; + final String virtualInstance = "virtual"; + final boolean isVirtualHalPresent = + faceSensorConfigurations.doesInstanceExist(virtualInstance); + if (Flags.faceVhalFeature() && Utils.isVirtualEnabled(getContext())) { + if (isVirtualHalPresent) { + return new Pair<>(virtualInstance, + faceSensorConfigurations.getSensorPropForInstance(virtualInstance)); } else { Slog.e(TAG, "Could not find virtual interface while it is enabled"); - return finalSensorPair; + return new Pair<>(finalSensorInstance, + faceSensorConfigurations.getSensorPropForInstance(finalSensorInstance)); } } else { - if (virtualSensorProps != null) { - return faceSensorConfigurations.getSensorPairNotForInstance("virtual"); + if (isVirtualHalPresent) { + final String notAVirtualInstance = + faceSensorConfigurations.getSensorNameNotForInstance(virtualInstance); + if (notAVirtualInstance != null) { + return new Pair<>(notAVirtualInstance, faceSensorConfigurations + .getSensorPropForInstance(notAVirtualInstance)); + } } } - return finalSensorPair; + return new Pair<>(finalSensorInstance, faceSensorConfigurations + .getSensorPropForInstance(finalSensorInstance)); } private Pair<List<FaceSensorPropertiesInternal>, List<String>> 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 1ba12134ab29..2dc03ed94b6c 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 @@ -1128,27 +1128,36 @@ public class FingerprintService extends SystemService { @NonNull private Pair<String, SensorProps[]> filterAvailableHalInstances( FingerprintSensorConfigurations fingerprintSensorConfigurations) { - Pair<String, SensorProps[]> finalSensorPair = - fingerprintSensorConfigurations.getSensorPair(); + final String finalSensorInstance = fingerprintSensorConfigurations.getSensorInstance(); if (fingerprintSensorConfigurations.isSingleSensorConfigurationPresent()) { - return finalSensorPair; + return new Pair<>(finalSensorInstance, + fingerprintSensorConfigurations.getSensorPropForInstance(finalSensorInstance)); } - - final Pair<String, SensorProps[]> virtualSensorPropsPair = fingerprintSensorConfigurations - .getSensorPairForInstance("virtual"); + final String virtualInstance = "virtual"; + final boolean isVirtualHalPresent = + fingerprintSensorConfigurations.doesInstanceExist(virtualInstance); if (Utils.isVirtualEnabled(getContext())) { - if (virtualSensorPropsPair != null) { - return virtualSensorPropsPair; + if (isVirtualHalPresent) { + return new Pair<>(virtualInstance, + fingerprintSensorConfigurations.getSensorPropForInstance(virtualInstance)); } else { Slog.e(TAG, "Could not find virtual interface while it is enabled"); - return finalSensorPair; + return new Pair<>(finalSensorInstance, + fingerprintSensorConfigurations.getSensorPropForInstance( + finalSensorInstance)); } } else { - if (virtualSensorPropsPair != null) { - return fingerprintSensorConfigurations.getSensorPairNotForInstance("virtual"); + if (isVirtualHalPresent) { + final String notAVirtualInstance = fingerprintSensorConfigurations + .getSensorNameNotForInstance(virtualInstance); + if (notAVirtualInstance != null) { + return new Pair<>(notAVirtualInstance, fingerprintSensorConfigurations + .getSensorPropForInstance(notAVirtualInstance)); + } } } - return finalSensorPair; + return new Pair<>(finalSensorInstance, fingerprintSensorConfigurations + .getSensorPropForInstance(finalSensorInstance)); } private Pair<List<FingerprintSensorPropertiesInternal>, List<String>> diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index ba21a327d837..434985e96d3e 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -464,10 +464,11 @@ public final class DisplayManagerService extends SystemService { // May be used outside of the lock but only on the handler thread. private final ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>(); - // Pending callback records indexed by calling process uid. + // Pending callback records indexed by calling process uid and pid. // Must be used outside of the lock mSyncRoot and should be selflocked. @GuardedBy("mPendingCallbackSelfLocked") - public final SparseArray<PendingCallback> mPendingCallbackSelfLocked = new SparseArray<>(); + public final SparseArray<SparseArray<PendingCallback>> mPendingCallbackSelfLocked = + new SparseArray<>(); // Temporary viewports, used when sending new viewport information to the // input system. May be used outside of the lock but only on the handler thread. @@ -1011,8 +1012,8 @@ public final class DisplayManagerService extends SystemService { } // Do we care about this uid? - PendingCallback pendingCallback = mPendingCallbackSelfLocked.get(uid); - if (pendingCallback == null) { + SparseArray<PendingCallback> pendingCallbacks = mPendingCallbackSelfLocked.get(uid); + if (pendingCallbacks == null) { return; } @@ -1020,7 +1021,12 @@ public final class DisplayManagerService extends SystemService { if (DEBUG) { Slog.d(TAG, "Uid " + uid + " becomes " + importance); } - pendingCallback.sendPendingDisplayEvent(); + for (int i = 0; i < pendingCallbacks.size(); i++) { + PendingCallback pendingCallback = pendingCallbacks.valueAt(i); + if (pendingCallback != null) { + pendingCallback.sendPendingDisplayEvent(); + } + } mPendingCallbackSelfLocked.delete(uid); } } @@ -3193,16 +3199,23 @@ public final class DisplayManagerService extends SystemService { for (int i = 0; i < mTempCallbacks.size(); i++) { CallbackRecord callbackRecord = mTempCallbacks.get(i); final int uid = callbackRecord.mUid; + final int pid = callbackRecord.mPid; if (isUidCached(uid)) { // For cached apps, save the pending event until it becomes non-cached synchronized (mPendingCallbackSelfLocked) { - PendingCallback pendingCallback = mPendingCallbackSelfLocked.get(uid); + SparseArray<PendingCallback> pendingCallbacks = mPendingCallbackSelfLocked.get( + uid); if (extraLogging(callbackRecord.mPackageName)) { - Slog.i(TAG, - "Uid is cached: " + uid + ", pendingCallback: " + pendingCallback); + Slog.i(TAG, "Uid is cached: " + uid + + ", pendingCallbacks: " + pendingCallbacks); + } + if (pendingCallbacks == null) { + pendingCallbacks = new SparseArray<>(); + mPendingCallbackSelfLocked.put(uid, pendingCallbacks); } + PendingCallback pendingCallback = pendingCallbacks.get(pid); if (pendingCallback == null) { - mPendingCallbackSelfLocked.put(uid, + pendingCallbacks.put(pid, new PendingCallback(callbackRecord, displayId, event)); } else { pendingCallback.addDisplayEvent(displayId, event); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index fd3da85e4437..4da280bf5c7b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2446,7 +2446,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService ComponentName intentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr(computer); ComponentName domainVerificationAgent = - getDomainVerificationAgentComponentNameLPr(computer); + getDomainVerificationAgentComponentNameLPr(computer, UserHandle.USER_SYSTEM); DomainVerificationProxy domainVerificationProxy = DomainVerificationProxy.makeProxy( intentFilterVerifierComponent, domainVerificationAgent, mContext, @@ -2754,12 +2754,13 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Nullable - private ComponentName getDomainVerificationAgentComponentNameLPr(@NonNull Computer computer) { + private ComponentName getDomainVerificationAgentComponentNameLPr(@NonNull Computer computer, + int userId) { Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION); List<ResolveInfo> matches = mResolveIntentHelper.queryIntentReceiversInternal(computer, intent, null, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, - UserHandle.USER_SYSTEM, Binder.getCallingUid()); + userId, Binder.getCallingUid()); ResolveInfo best = null; final int N = matches.size(); for (int i = 0; i < N; i++) { @@ -2767,7 +2768,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService final String packageName = cur.getComponentInfo().packageName; if (checkPermission( android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, packageName, - UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) { + userId) != PackageManager.PERMISSION_GRANTED) { Slog.w(TAG, "Domain verification agent found but does not hold permission: " + packageName); continue; @@ -2775,7 +2776,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (best == null || cur.priority > best.priority) { if (computer.isComponentEffectivelyEnabled(cur.getComponentInfo(), - UserHandle.SYSTEM)) { + UserHandle.of(userId))) { best = cur; } else { Slog.w(TAG, "Domain verification agent found but not enabled"); @@ -6512,13 +6513,13 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override @Nullable - public ComponentName getDomainVerificationAgent() { + public ComponentName getDomainVerificationAgent(int userId) { final int callerUid = Binder.getCallingUid(); if (!PackageManagerServiceUtils.isRootOrShell(callerUid)) { throw new SecurityException("Not allowed to query domain verification agent"); } final Computer snapshot = snapshotComputer(); - return getDomainVerificationAgentComponentNameLPr(snapshot); + return getDomainVerificationAgentComponentNameLPr(snapshot, userId); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index a9e1725ea9a0..59faf24aa77a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -4412,8 +4412,31 @@ class PackageManagerShellCommand extends ShellCommand { private int runGetDomainVerificationAgent() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) { + UserManagerInternal umi = + LocalServices.getService(UserManagerInternal.class); + UserInfo userInfo = umi.getUserInfo(userId); + if (userInfo == null) { + pw.println("Failure [user " + userId + " doesn't exist]"); + return 1; + } + } + } else { + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_SYSTEM, "runGetDomainVerificationAgent"); try { - final ComponentName domainVerificationAgent = mInterface.getDomainVerificationAgent(); + final ComponentName domainVerificationAgent = + mInterface.getDomainVerificationAgent(translatedUserId); pw.println(domainVerificationAgent == null ? "No Domain Verifier available!" : domainVerificationAgent.flattenToString()); } catch (Exception e) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index f5ed8d4af45b..9f2c36a1c34a 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -93,6 +93,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; +import com.android.internal.pm.parsing.pkg.AndroidPackageInternal; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedPermission; @@ -911,8 +912,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile sharedUserSetting.mDisabledPackages.remove(p); } p.getPkgState().setUpdatedSystemApp(false); + final AndroidPackageInternal pkg = p.getPkg(); PackageSetting ret = addPackageLPw(name, p.getRealName(), p.getPath(), p.getAppId(), - p.getFlags(), p.getPrivateFlags(), mDomainVerificationManager.generateNewId()); + p.getFlags(), p.getPrivateFlags(), mDomainVerificationManager.generateNewId(), + pkg == null ? false : pkg.isSdkLibrary()); if (ret != null) { ret.setLegacyNativeLibraryPath(p.getLegacyNativeLibraryPath()); ret.setPrimaryCpuAbi(p.getPrimaryCpuAbiLegacy()); @@ -951,8 +954,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } } - PackageSetting addPackageLPw(String name, String realName, File codePath, int uid, int pkgFlags, - int pkgPrivateFlags, @NonNull UUID domainSetId) { + PackageSetting addPackageLPw(String name, String realName, File codePath, int uid, + int pkgFlags, int pkgPrivateFlags, @NonNull UUID domainSetId, boolean isSdkLibrary) { PackageSetting p = mPackages.get(name); if (p != null) { if (p.getAppId() == uid) { @@ -964,7 +967,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } p = new PackageSetting(name, realName, codePath, pkgFlags, pkgPrivateFlags, domainSetId) .setAppId(uid); - if (mAppIds.registerExistingAppId(uid, p, name)) { + if ((uid == Process.INVALID_UID && isSdkLibrary && Flags.disallowSdkLibsToBeApps()) + || mAppIds.registerExistingAppId(uid, p, name)) { mPackages.put(name, p); return p; } @@ -4176,7 +4180,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } else if (appId > 0 || (appId == Process.INVALID_UID && isSdkLibrary && Flags.disallowSdkLibsToBeApps())) { packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr), - appId, pkgFlags, pkgPrivateFlags, domainSetId); + appId, pkgFlags, pkgPrivateFlags, domainSetId, isSdkLibrary); if (PackageManagerService.DEBUG_SETTINGS) Log.i(PackageManagerService.TAG, "Reading package " + name + ": appId=" + appId + " pkg=" + packageSetting); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index f5ac8306cfa9..63386a999d40 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -567,7 +567,7 @@ public class UserManagerService extends IUserManager.Stub { int autoLockPreference = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.PRIVATE_SPACE_AUTO_LOCK, - Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_NEVER, + Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART, getMainUserIdUnchecked()); Slog.i(LOG_TAG, "Auto-lock settings changed to " + autoLockPreference); setOrUpdateAutoLockPreferenceForPrivateProfile(autoLockPreference); @@ -615,7 +615,7 @@ public class UserManagerService extends IUserManager.Stub { int privateSpaceAutoLockPreference = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.PRIVATE_SPACE_AUTO_LOCK, - Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_NEVER, + Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART, getMainUserIdUnchecked()); if (privateSpaceAutoLockPreference != Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY) { @@ -714,7 +714,7 @@ public class UserManagerService extends IUserManager.Stub { if (isAutoLockForPrivateSpaceEnabled()) { int autoLockPreference = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.PRIVATE_SPACE_AUTO_LOCK, - Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_NEVER, + Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART, getMainUserIdUnchecked()); boolean isAutoLockOnDeviceLockSelected = autoLockPreference == Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK; @@ -1052,7 +1052,8 @@ public class UserManagerService extends IUserManager.Stub { setOrUpdateAutoLockPreferenceForPrivateProfile( Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.PRIVATE_SPACE_AUTO_LOCK, - Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_NEVER, mainUserId)); + Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART, + mainUserId)); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c3175d64e2d3..318042e87387 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -9030,7 +9030,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { void sendDeviceOwnerOrProfileOwnerCommand(String action, Bundle extras, int userId) { if (userId == UserHandle.USER_ALL) { - userId = UserHandle.USER_SYSTEM; + if (Flags.headlessDeviceOwnerDelegateSecurityLoggingBugFix() + && getHeadlessDeviceOwnerModeForDeviceOwner() + == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) { + userId = mOwners.getDeviceOwnerUserId(); + } else { + userId = UserHandle.USER_SYSTEM; + } } boolean inForeground = false; ComponentName receiverComponent = null; diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java index 2366f56707fa..7aafa8e92690 100644 --- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java @@ -24,9 +24,11 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import android.content.pm.PackageManagerInternal; import android.media.projection.MediaProjectionInfo; import android.media.projection.MediaProjectionManager; import android.os.Binder; @@ -72,6 +74,7 @@ public class SensitiveContentProtectionManagerServiceContentTest { @Mock private WindowManagerInternal mWindowManager; @Mock private MediaProjectionManager mProjectionManager; + @Mock private PackageManagerInternal mPackageManagerInternal; private MediaProjectionInfo mMediaProjectionInfo; @Captor @@ -91,7 +94,7 @@ public class SensitiveContentProtectionManagerServiceContentTest { mSensitiveContentProtectionManagerService = new SensitiveContentProtectionManagerService(mContext); mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager, - new ArraySet<>(Set.of(mExemptedScreenRecorderPackage))); + mPackageManagerInternal, new ArraySet<>(Set.of(mExemptedScreenRecorderPackage))); verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any()); mMediaPorjectionCallback = mMediaProjectionCallbackCaptor.getValue(); mMediaProjectionInfo = @@ -146,6 +149,20 @@ public class SensitiveContentProtectionManagerServiceContentTest { } @Test + public void testAutofillServicePackageExemption() { + String testAutofillService = mScreenRecorderPackage + "/com.example.SampleAutofillService"; + int userId = Process.myUserHandle().getIdentifier(); + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.AUTOFILL_SERVICE, testAutofillService , userId); + + mMediaPorjectionCallback.onStart(mMediaProjectionInfo); + mSensitiveContentProtectionManagerService.setSensitiveContentProtection( + mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true); + verify(mWindowManager, never()) + .addBlockScreenCaptureForApps(mPackageInfoCaptor.capture()); + } + + @Test public void testDeveloperOptionDisableFeature() { mockDisabledViaDeveloperOption(); mMediaProjectionCallbackCaptor.getValue().onStart(mMediaProjectionInfo); diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java index e74fe296d0a5..506514469338 100644 --- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import android.content.pm.PackageManagerInternal; import android.media.projection.MediaProjectionInfo; import android.media.projection.MediaProjectionManager; import android.platform.test.annotations.RequiresFlagsEnabled; @@ -104,6 +105,9 @@ public class SensitiveContentProtectionManagerServiceNotificationTest { private WindowManagerInternal mWindowManager; @Mock + private PackageManagerInternal mPackageManagerInternal; + + @Mock private StatusBarNotification mNotification1; @Mock @@ -141,7 +145,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest { setupSensitiveNotification(); mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager, - new ArraySet<>(Set.of(EXEMPTED_SCREEN_RECORDER_PACKAGE))); + mPackageManagerInternal, new ArraySet<>(Set.of(EXEMPTED_SCREEN_RECORDER_PACKAGE))); // Obtain useful mMediaProjectionCallback verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any()); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 538c0ee52424..c9aab5318840 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -166,7 +166,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { null } whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(), - nullable(), nullable(), nullable())) { + nullable(), nullable(), nullable(), nullable())) { val name: String = getArgument(0) val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name } ?: return@whenever null diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java index 6aa1825ba6b7..759a974bd41f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -736,9 +736,10 @@ public final class UserManagerServiceTest { Mockito.clearInvocations(mKeyguardManager); Mockito.clearInvocations(mSpiedContext); - // Finally, set the preference to don't auto-lock + // Finally, set the preference to auto-lock only after device restart, which is the default + // behaviour mUms.setOrUpdateAutoLockPreferenceForPrivateProfile( - Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_NEVER); + Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART); // Verify that inactivity broadcasts are unregistered and keyguard listener was removed Mockito.verify(mSpiedContext).unregisterReceiver(any()); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java index f0dc5f0606f3..7e04277fddc0 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java @@ -269,7 +269,7 @@ public class AuthServiceTest { mFingerprintSensorConfigurationsCaptor.capture()); final SensorProps[] fingerprintProp = mFingerprintSensorConfigurationsCaptor.getValue() - .getSensorPairForInstance("defaultHIDL").second; + .getSensorPropForInstance("defaultHIDL"); assertEquals(fingerprintProp[0].commonProps.sensorId, fingerprintId); assertEquals(fingerprintProp[0].commonProps.sensorStrength, @@ -280,7 +280,7 @@ public class AuthServiceTest { final android.hardware.biometrics.face.SensorProps[] faceProp = mFaceSensorConfigurationsCaptor.getValue() - .getSensorPairForInstance("defaultHIDL").second; + .getSensorPropForInstance("defaultHIDL"); assertEquals(faceProp[0].commonProps.sensorId, faceId); assertEquals(faceProp[0].commonProps.sensorStrength, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java index 3aaac2e9cf1b..e015e97debb0 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java @@ -32,8 +32,6 @@ import android.content.ComponentName; import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.IBiometricService; -import android.hardware.biometrics.face.IFace; -import android.hardware.biometrics.face.SensorProps; import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceSensorConfigurations; import android.hardware.face.FaceSensorPropertiesInternal; @@ -93,18 +91,12 @@ public class FaceServiceTest { @Mock private FaceProvider mFaceProviderVirtual; @Mock - private IFace mDefaultFaceDaemon; - @Mock - private IFace mVirtualFaceDaemon; - @Mock private IBiometricService mIBiometricService; @Mock private IBinder mToken; @Mock private IFaceServiceReceiver mFaceServiceReceiver; - private final SensorProps mDefaultSensorProps = new SensorProps(); - private final SensorProps mVirtualSensorProps = new SensorProps(); private FaceService mFaceService; private final FaceSensorPropertiesInternal mSensorPropsDefault = new FaceSensorPropertiesInternal(ID_DEFAULT, STRENGTH_STRONG, @@ -126,10 +118,6 @@ public class FaceServiceTest { @Before public void setUp() throws RemoteException { - when(mDefaultFaceDaemon.getSensorProps()).thenReturn( - new SensorProps[]{mDefaultSensorProps}); - when(mVirtualFaceDaemon.getSensorProps()).thenReturn( - new SensorProps[]{mVirtualSensorProps}); when(mFaceProviderDefault.getSensorProperties()).thenReturn(List.of(mSensorPropsDefault)); when(mFaceProviderVirtual.getSensorProperties()).thenReturn(List.of(mSensorPropsVirtual)); when(mFaceProviderDefault.containsSensor(anyInt())) @@ -140,15 +128,7 @@ public class FaceServiceTest { mContext.getTestablePermissions().setPermission( USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_GRANTED); mFaceSensorConfigurations = new FaceSensorConfigurations(false); - mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_DEFAULT, NAME_VIRTUAL}, - (name) -> { - if (name.equals(IFace.DESCRIPTOR + "/" + NAME_DEFAULT)) { - return mDefaultFaceDaemon; - } else if (name.equals(IFace.DESCRIPTOR + "/" + NAME_VIRTUAL)) { - return mVirtualFaceDaemon; - } - return null; - }); + mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_DEFAULT, NAME_VIRTUAL}); } private void initService() { @@ -199,15 +179,7 @@ public class FaceServiceTest { @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) public void registerAuthenticatorsLegacy_virtualAlwaysWhenNoOther() throws Exception { mFaceSensorConfigurations = new FaceSensorConfigurations(false); - mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_VIRTUAL}, - (name) -> { - if (name.equals(IFace.DESCRIPTOR + "/" + NAME_DEFAULT)) { - return mDefaultFaceDaemon; - } else if (name.equals(IFace.DESCRIPTOR + "/" + NAME_VIRTUAL)) { - return mVirtualFaceDaemon; - } - return null; - }); + mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_VIRTUAL}); initService(); mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java index 88956b614eae..20961a9ddddd 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java @@ -43,8 +43,6 @@ import android.app.AppOpsManager; import android.content.ComponentName; import android.content.pm.PackageManager; import android.hardware.biometrics.IBiometricService; -import android.hardware.biometrics.fingerprint.IFingerprint; -import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.fingerprint.FingerprintAuthenticateOptions; import android.hardware.fingerprint.FingerprintSensorConfigurations; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -123,10 +121,6 @@ public class FingerprintServiceTest { private IBinder mToken; @Mock private VirtualDeviceManagerInternal mVdmInternal; - @Mock - private IFingerprint mDefaultFingerprintDaemon; - @Mock - private IFingerprint mVirtualFingerprintDaemon; @Captor private ArgumentCaptor<FingerprintAuthenticateOptions> mAuthenticateOptionsCaptor; @@ -145,8 +139,6 @@ public class FingerprintServiceTest { false /* resetLockoutRequiresHardwareAuthToken */); private FingerprintSensorConfigurations mFingerprintSensorConfigurations; private FingerprintService mService; - private final SensorProps mDefaultSensorProps = new SensorProps(); - private final SensorProps mVirtualSensorProps = new SensorProps(); @Before public void setup() throws Exception { @@ -159,10 +151,6 @@ public class FingerprintServiceTest { .thenAnswer(i -> i.getArguments()[0].equals(ID_DEFAULT)); when(mFingerprintVirtual.containsSensor(anyInt())) .thenAnswer(i -> i.getArguments()[0].equals(ID_VIRTUAL)); - when(mDefaultFingerprintDaemon.getSensorProps()).thenReturn( - new SensorProps[]{mDefaultSensorProps}); - when(mVirtualFingerprintDaemon.getSensorProps()).thenReturn( - new SensorProps[]{mVirtualSensorProps}); mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager); for (int permission : List.of(OP_USE_BIOMETRIC, OP_USE_FINGERPRINT)) { @@ -177,15 +165,7 @@ public class FingerprintServiceTest { mFingerprintSensorConfigurations = new FingerprintSensorConfigurations( true /* resetLockoutRequiresHardwareAuthToken */); - mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_DEFAULT, NAME_VIRTUAL}, - (name) -> { - if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_DEFAULT)) { - return mDefaultFingerprintDaemon; - } else if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_VIRTUAL)) { - return mVirtualFingerprintDaemon; - } - return null; - }); + mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_DEFAULT, NAME_VIRTUAL}); } private void initServiceWith(String... aidlInstances) { @@ -270,15 +250,7 @@ public class FingerprintServiceTest { public void registerAuthenticatorsLegacy_virtualAlwaysWhenNoOther() throws Exception { mFingerprintSensorConfigurations = new FingerprintSensorConfigurations(true); - mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_VIRTUAL}, - (name) -> { - if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_DEFAULT)) { - return mDefaultFingerprintDaemon; - } else if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_VIRTUAL)) { - return mVirtualFingerprintDaemon; - } - return null; - }); + mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_VIRTUAL}); initServiceWith(NAME_VIRTUAL); mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index ed3d5d546f72..ebdd556428b5 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -8013,6 +8013,27 @@ public class CarrierConfigManager { KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL = KEY_PREFIX + "scan_limited_service_after_volte_failure_bool"; + /** + * This config defines {@link ImsReasonInfo} code with which the emergency call + * shall be retried. + * + * <p> + * If the reason code is one of the following, the emergency call shall be retried + * regardless of this configuration. + * <ul> + * <li>{@link ImsReasonInfo#CODE_LOCAL_CALL_CS_RETRY_REQUIRED}</li> + * <li>{@link ImsReasonInfo#CODE_LOCAL_NOT_REGISTERED}</li> + * <li>{@link ImsReasonInfo#CODE_SIP_ALTERNATE_EMERGENCY_CALL}</li> + * </ul> + * <p> + * + * This config is empty by default. + * + * @hide + */ + public static final String KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY = + KEY_PREFIX + "ims_reasoninfo_code_to_retry_emergency_int_array"; + private static PersistableBundle getDefaults() { PersistableBundle defaults = new PersistableBundle(); defaults.putBoolean(KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL, false); @@ -8085,6 +8106,8 @@ public class CarrierConfigManager { defaults.putBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL, true); defaults.putBoolean(KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL, false); + defaults.putIntArray(KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY, + new int[0]); return defaults; } @@ -9836,7 +9859,7 @@ public class CarrierConfigManager { * An integer key holds the time interval for refreshing or re-querying the satellite * entitlement status from the entitlement server to ensure it is the latest. * - * The default value is 30 days (1 month). + * The default value is 7 days. */ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = @@ -11008,7 +11031,7 @@ public class CarrierConfigManager { CellSignalStrengthLte.USE_RSRP); sDefaults.putBoolean(KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true); sDefaults.putBoolean(KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, true); - sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 30); + sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 7); sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false); sDefaults.putString(KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING, "androidSatmode"); sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp index 5a0f742372d7..1d3e4bd531b4 100644 --- a/wifi/tests/Android.bp +++ b/wifi/tests/Android.bp @@ -39,7 +39,7 @@ android_test { "androidx.test.core", "frameworks-base-testutils", "guava", - "mockito-target-minus-junit4", + "mockito-target-extended-minus-junit4", "truth", ], @@ -48,6 +48,12 @@ android_test { "android.test.base", ], + // Required by Extended Mockito + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + test_suites: [ "general-tests", ], diff --git a/wifi/tests/AndroidManifest.xml b/wifi/tests/AndroidManifest.xml index 18986fc5c603..66056e5cab38 100644 --- a/wifi/tests/AndroidManifest.xml +++ b/wifi/tests/AndroidManifest.xml @@ -18,7 +18,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.net.wifi.nonupdatable.test"> - <application> + <application android:debuggable="true"> <uses-library android:name="android.test.runner"/> <activity android:label="WifiTestDummyLabel" android:name="WifiTestDummyName" |