diff options
20 files changed, 285 insertions, 145 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index bd00c03741f3..b897c1acd17f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -4783,6 +4783,10 @@ public class JobSchedulerService extends com.android.server.SystemService * Returns whether the app holds the {@link Manifest.permission.RUN_BACKUP_JOBS} permission. */ private boolean hasRunBackupJobsPermission(@NonNull String packageName, int packageUid) { + // This permission is currently hidden so always return false for now (see b/331272951) + return false; + + /** if (packageName == null) { Slog.wtfStack(TAG, "Expected a non-null package name when calling hasRunBackupJobsPermission"); @@ -4793,6 +4797,7 @@ public class JobSchedulerService extends com.android.server.SystemService android.Manifest.permission.RUN_BACKUP_JOBS, PermissionChecker.PID_UNKNOWN, packageUid, packageName) == PermissionChecker.PERMISSION_GRANTED; + */ } /** diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index edd86e3454a5..d643768185e3 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -1213,7 +1213,8 @@ public final class JobStatus { return ACTIVE_INDEX; } - final boolean isEligibleAsBackupJob = job.getTriggerContentUris() != null + final boolean isEligibleAsBackupJob = false // this exemption is being disabled for now. + && job.getTriggerContentUris() != null && job.getRequiredNetwork() != null && !job.hasLateConstraint() && mJobSchedulerInternal.hasRunBackupJobsPermission(sourcePackageName, sourceUid); diff --git a/core/api/current.txt b/core/api/current.txt index 218fbebd66c0..950f2da02599 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -285,7 +285,6 @@ package android { field @FlaggedApi("android.companion.flags.device_presence") public static final String REQUEST_OBSERVE_DEVICE_UUID_PRESENCE = "android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE"; field public static final String REQUEST_PASSWORD_COMPLEXITY = "android.permission.REQUEST_PASSWORD_COMPLEXITY"; field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES"; - field @FlaggedApi("android.app.job.backup_jobs_exemption") public static final String RUN_BACKUP_JOBS = "android.permission.RUN_BACKUP_JOBS"; field public static final String RUN_USER_INITIATED_JOBS = "android.permission.RUN_USER_INITIATED_JOBS"; field public static final String SCHEDULE_EXACT_ALARM = "android.permission.SCHEDULE_EXACT_ALARM"; field public static final String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE"; @@ -37338,7 +37337,6 @@ package android.provider { field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final String ACTION_REQUEST_MANAGE_MEDIA = "android.settings.REQUEST_MANAGE_MEDIA"; field @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") public static final String ACTION_REQUEST_MEDIA_ROUTING_CONTROL = "android.settings.REQUEST_MEDIA_ROUTING_CONTROL"; - field @FlaggedApi("android.provider.backup_tasks_settings_screen") public static final String ACTION_REQUEST_RUN_BACKUP_JOBS = "android.settings.REQUEST_RUN_BACKUP_JOBS"; field public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM = "android.settings.REQUEST_SCHEDULE_EXACT_ALARM"; field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String ACTION_SATELLITE_SETTING = "android.settings.SATELLITE_SETTING"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 702a47ef54ec..5547028e489f 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2178,8 +2178,15 @@ package android.app.contentsuggestions { package android.app.contextualsearch { + @FlaggedApi("android.app.contextualsearch.flags.enable_service") public final class CallbackToken implements android.os.Parcelable { + ctor public CallbackToken(); + method public int describeContents(); + method public void getContextualSearchState(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.contextualsearch.ContextualSearchState,java.lang.Throwable>); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.contextualsearch.CallbackToken> CREATOR; + } + @FlaggedApi("android.app.contextualsearch.flags.enable_service") public class ContextualSearchManager { - method public void getContextualSearchState(@NonNull android.os.IBinder, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.contextualsearch.ContextualSearchState,java.lang.Throwable>); method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH) public void startContextualSearch(int); field public static final String ACTION_LAUNCH_CONTEXTUAL_SEARCH = "android.app.contextualsearch.action.LAUNCH_CONTEXTUAL_SEARCH"; field public static final int ENTRYPOINT_LONG_PRESS_HOME = 2; // 0x2 diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 962f4f738793..0d4668877c05 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -803,6 +803,14 @@ package android.app.contentsuggestions { } +package android.app.contextualsearch { + + @FlaggedApi("android.app.contextualsearch.flags.enable_service") public final class CallbackToken implements android.os.Parcelable { + method @NonNull public android.os.IBinder getToken(); + } + +} + package android.app.job { public class JobParameters implements android.os.Parcelable { diff --git a/core/java/android/app/contextualsearch/CallbackToken.java b/core/java/android/app/contextualsearch/CallbackToken.java new file mode 100644 index 000000000000..0bbd1e546e5d --- /dev/null +++ b/core/java/android/app/contextualsearch/CallbackToken.java @@ -0,0 +1,159 @@ +/* + * 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 android.app.contextualsearch; + +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.app.contextualsearch.flags.Flags; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.OutcomeReceiver; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelableException; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.util.concurrent.Executor; + +/** + * Used to share a single use token with the contextual search handling activity via the launch + * extras bundle. + * The caller can then use this token to get {@link ContextualSearchState} by calling + * {@link #getContextualSearchState}. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_ENABLE_SERVICE) +@SystemApi +public final class CallbackToken implements Parcelable { + private static final boolean DEBUG = true; + private static final String TAG = CallbackToken.class.getSimpleName(); + private final IBinder mToken; + + private boolean mTokenUsed = false; + + public CallbackToken() { + mToken = new Binder(); + } + + private CallbackToken(Parcel in) { + mToken = in.readStrongBinder(); + } + + /** + * Returns the {@link ContextualSearchState} to the handler via the provided callback. The + * method can only be invoked to provide the {@link OutcomeReceiver} once and all subsequent + * invocations of this method will result in {@link OutcomeReceiver#onError} being called with + * an {@link IllegalAccessException}. + * + * @param executor The executor which will be used to invoke the callback. + * @param callback The callback which will be used to return {@link ContextualSearchState} + * if/when it is available via {@link OutcomeReceiver#onResult}. It will also be + * used to return errors via {@link OutcomeReceiver#onError}. + */ + public void getContextualSearchState(@NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<ContextualSearchState, Throwable> callback) { + if (DEBUG) Log.d(TAG, "getContextualSearchState for token:" + mToken); + if (mTokenUsed) { + callback.onError(new IllegalAccessException("Token already used.")); + } + mTokenUsed = true; + try { + // Get the service from the system server. + IBinder b = ServiceManager.getService(Context.CONTEXTUAL_SEARCH_SERVICE); + IContextualSearchManager service = IContextualSearchManager.Stub.asInterface(b); + final CallbackWrapper wrapper = new CallbackWrapper(executor, callback); + // If the service is not null, hand over the call to the service. + if (service != null) { + service.getContextualSearchState(mToken, wrapper); + } else { + Log.w(TAG, "Failed to getContextualSearchState. Service null."); + } + } catch (RemoteException e) { + if (DEBUG) Log.d(TAG, "Failed to call getContextualSearchState", e); + e.rethrowFromSystemServer(); + } + } + + /** + * Return the token necessary for validating the caller of {@link #getContextualSearchState}. + * + * @hide + */ + @TestApi + @NonNull + public IBinder getToken() { + return mToken; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + } + + @NonNull + public static final Creator<CallbackToken> CREATOR = new Creator<>() { + @Override + public CallbackToken createFromParcel(Parcel in) { + return new CallbackToken(in); + } + + @Override + public CallbackToken[] newArray(int size) { + return new CallbackToken[size]; + } + }; + + private static class CallbackWrapper extends IContextualSearchCallback.Stub { + private final OutcomeReceiver<ContextualSearchState, Throwable> mCallback; + private final Executor mExecutor; + + CallbackWrapper(@NonNull Executor callbackExecutor, + @NonNull OutcomeReceiver<ContextualSearchState, Throwable> callback) { + mCallback = callback; + mExecutor = callbackExecutor; + } + + @Override + public void onResult(ContextualSearchState state) { + Binder.withCleanCallingIdentity(() -> { + if (DEBUG) Log.d(TAG, "onResult state:" + state); + mExecutor.execute(() -> mCallback.onResult(state)); + }); + } + + @Override + public void onError(ParcelableException error) { + Binder.withCleanCallingIdentity(() -> { + if (DEBUG) Log.w(TAG, "onError", error); + mExecutor.execute(() -> mCallback.onError(error)); + }); + } + } +} diff --git a/core/java/android/app/contextualsearch/ContextualSearchManager.java b/core/java/android/app/contextualsearch/ContextualSearchManager.java index 693de219e7d7..a894a0e27b95 100644 --- a/core/java/android/app/contextualsearch/ContextualSearchManager.java +++ b/core/java/android/app/contextualsearch/ContextualSearchManager.java @@ -18,37 +18,26 @@ package android.app.contextualsearch; import static android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH; -import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; -import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.contextualsearch.flags.Flags; import android.content.Context; -import android.os.Binder; -import android.os.Bundle; import android.os.IBinder; -import android.os.OutcomeReceiver; -import android.os.ParcelableException; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.concurrent.Executor; /** * {@link ContextualSearchManager} is a system service to facilitate contextual search experience on * configured Android devices. * <p> - * This class lets - * <ul> - * <li> a caller start contextual search by calling {@link #startContextualSearch} method. - * <li> a handler request {@link ContextualSearchState} by calling the - * {@link #getContextualSearchState} method. - * </ul> + * This class lets a caller start contextual search by calling {@link #startContextualSearch} + * method. * * @hide */ @@ -92,7 +81,7 @@ public class ContextualSearchManager { /** * Key to get the binder token from the extras of the activity launched by contextual search. - * This token is needed to invoke {@link #getContextualSearchState} method. + * This token is needed to invoke {@link CallbackToken#getContextualSearchState} method. * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH. */ public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN"; @@ -174,57 +163,4 @@ public class ContextualSearchManager { e.rethrowFromSystemServer(); } } - - /** - * Returns the {@link ContextualSearchState} to the handler via the provided callback. - * - * @param token The caller is expected to get the token from the launch extras of the handling - * activity using {@link Bundle#getIBinder} with {@link #EXTRA_TOKEN} key. - * <br> - * <b>Note</b> This token is for one time use only. Subsequent uses will invoke - * callback's {@link OutcomeReceiver#onError}. - * @param executor The executor which will be used to invoke the callback. - * @param callback The callback which will be used to return {@link ContextualSearchState} - * if/when it is available via {@link OutcomeReceiver#onResult}. It will also be - * used to return errors via {@link OutcomeReceiver#onError}. - */ - public void getContextualSearchState(@NonNull IBinder token, - @NonNull @CallbackExecutor Executor executor, - @NonNull OutcomeReceiver<ContextualSearchState, Throwable> callback) { - if (DEBUG) Log.d(TAG, "getContextualSearchState for token:" + token); - try { - final CallbackWrapper wrapper = new CallbackWrapper(executor, callback); - mService.getContextualSearchState(token, wrapper); - } catch (RemoteException e) { - if (DEBUG) Log.d(TAG, "Failed to getContextualSearchState", e); - e.rethrowFromSystemServer(); - } - } - - private static class CallbackWrapper extends IContextualSearchCallback.Stub { - private final OutcomeReceiver<ContextualSearchState, Throwable> mCallback; - private final Executor mExecutor; - - CallbackWrapper(@NonNull Executor callbackExecutor, - @NonNull OutcomeReceiver<ContextualSearchState, Throwable> callback) { - mCallback = callback; - mExecutor = callbackExecutor; - } - - @Override - public void onResult(ContextualSearchState state) { - Binder.withCleanCallingIdentity(() -> { - if (DEBUG) Log.d(TAG, "onResult state:" + state); - mExecutor.execute(() -> mCallback.onResult(state)); - }); - } - - @Override - public void onError(ParcelableException error) { - Binder.withCleanCallingIdentity(() -> { - if (DEBUG) Log.w(TAG, "onError", error); - mExecutor.execute(() -> mCallback.onError(error)); - }); - } - } } diff --git a/core/java/android/app/contextualsearch/ContextualSearchState.java b/core/java/android/app/contextualsearch/ContextualSearchState.java index 5c04bc8933c0..f01ae55d05b6 100644 --- a/core/java/android/app/contextualsearch/ContextualSearchState.java +++ b/core/java/android/app/contextualsearch/ContextualSearchState.java @@ -32,6 +32,10 @@ import androidx.annotation.NonNull; * {@link ContextualSearchState} contains additional data a contextual search handler can request * via {@link ContextualSearchManager#getContextualSearchState} method. * + * It provides the caller of {@link ContextualSearchManager#getContextualSearchState} with an + * {@link AssistStructure}, {@link AssistContent} and a {@link Bundle} extras containing any + * relevant data added by th system server. When invoked via the Launcher, this bundle is empty. + * * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_SERVICE) @@ -41,6 +45,12 @@ public final class ContextualSearchState implements Parcelable { private final @Nullable AssistStructure mStructure; private final @Nullable AssistContent mContent; + /** + * {@link ContextualSearchState} contains non-essential data which can be requested by the + * Contextual Search activity. + * The activity can request an instance of {@link ContextualSearchState} by calling + * {@link CallbackToken#getContextualSearchState} and passing a valid token as an argument. + */ public ContextualSearchState(@Nullable AssistStructure structure, @Nullable AssistContent content, @NonNull Bundle extras) { mStructure = structure; diff --git a/core/java/android/app/contextualsearch/IContextualSearchManager.aidl b/core/java/android/app/contextualsearch/IContextualSearchManager.aidl index 1735a7139e6f..9b0b8b775971 100644 --- a/core/java/android/app/contextualsearch/IContextualSearchManager.aidl +++ b/core/java/android/app/contextualsearch/IContextualSearchManager.aidl @@ -1,6 +1,5 @@ package android.app.contextualsearch; - import android.app.contextualsearch.IContextualSearchCallback; /** * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 324852248da0..c74d50a004bf 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -696,6 +696,8 @@ public final class Settings { * Output: When a package data uri is passed as input, the activity result is set to * {@link android.app.Activity#RESULT_OK} if the permission was granted to the app. Otherwise, * the result is set to {@link android.app.Activity#RESULT_CANCELED}. + * + * @hide */ @FlaggedApi(Flags.FLAG_BACKUP_TASKS_SETTINGS_SCREEN) @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6431db9d174e..cfe6f732e54b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7986,6 +7986,7 @@ content URI trigger and network constraint set. <p>This is a special access permission that can be revoked by the system or the user. <p>Protection level: signature|privileged|appop + @hide --> <permission android:name="android.permission.RUN_BACKUP_JOBS" android:protectionLevel="signature|privileged|appop"/> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt index 3fb915226963..7bb08d2c26e8 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt @@ -17,6 +17,8 @@ package com.android.credentialmanager.common.ui import android.content.Context +import android.util.Log +import com.android.credentialmanager.common.Constants import android.widget.RemoteViews import androidx.core.content.ContextCompat import com.android.credentialmanager.model.get.CredentialEntryInfo @@ -29,7 +31,8 @@ class RemoteViewsFactory { private const val SET_ADJUST_VIEW_BOUNDS_METHOD_NAME = "setAdjustViewBounds" private const val SET_MAX_HEIGHT_METHOD_NAME = "setMaxHeight" private const val SET_BACKGROUND_RESOURCE_METHOD_NAME = "setBackgroundResource" - private const val BULLET_POINT = "\u2022" + private const val SEPARATOR = " " + "\u2022" + " " + // TODO(jbabs): RemoteViews#setViewPadding renders this as 8dp on the display. Debug why. private const val END_ITEMS_PADDING = 28 @@ -43,20 +46,14 @@ class RemoteViewsFactory { var layoutId: Int = com.android.credentialmanager.R.layout .credman_dropdown_presentation_layout val remoteViews = RemoteViews(context.packageName, layoutId) - if (credentialEntryInfo.credentialType == CredentialType.UNKNOWN) { - return remoteViews - } val displayName = credentialEntryInfo.displayName ?: credentialEntryInfo.userName remoteViews.setTextViewText(android.R.id.text1, displayName) - val secondaryText = - if (credentialEntryInfo.displayName != null - && (credentialEntryInfo.displayName != credentialEntryInfo.userName)) - (credentialEntryInfo.userName + " " + BULLET_POINT + " " - + credentialEntryInfo.credentialTypeDisplayName - + " " + BULLET_POINT + " " + credentialEntryInfo.providerDisplayName) - else (credentialEntryInfo.credentialTypeDisplayName + " " + BULLET_POINT + " " - + credentialEntryInfo.providerDisplayName) - remoteViews.setTextViewText(android.R.id.text2, secondaryText) + val secondaryText = getSecondaryText(credentialEntryInfo) + if (secondaryText.isNullOrBlank()) { + Log.w(Constants.LOG_TAG, "Secondary text for dropdown is null") + } else { + remoteViews.setTextViewText(android.R.id.text2, secondaryText) + } remoteViews.setImageViewIcon(android.R.id.icon1, icon); remoteViews.setBoolean( android.R.id.icon1, SET_ADJUST_VIEW_BOUNDS_METHOD_NAME, true); @@ -88,6 +85,26 @@ class RemoteViewsFactory { return remoteViews } + /** + * Computes the secondary text for dropdown presentation based on available fields. + * + * <p> Format for secondary text is [username] . [credentialType] . [providerDisplayName] + * If display name and username are the same, we do not display username + * If credential type is missing as in the case with SiwG, we just display + * providerDisplayName. Both credential type and provider display name should not be empty. + */ + private fun getSecondaryText(credentialEntryInfo: CredentialEntryInfo): String? { + return listOf(if (credentialEntryInfo.displayName != null + && (credentialEntryInfo.displayName != credentialEntryInfo.userName)) + (credentialEntryInfo.userName) else null, + credentialEntryInfo.credentialTypeDisplayName, + credentialEntryInfo.providerDisplayName).filterNot { it.isNullOrBlank() } + .let { itemsToDisplay -> + if (itemsToDisplay.isEmpty()) null + else itemsToDisplay.joinToString(separator = SEPARATOR) + } + } + fun createMoreSignInOptionsPresentation(context: Context): RemoteViews { var layoutId: Int = com.android.credentialmanager.R.layout .credman_dropdown_bottom_sheet diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index 1adb3359b17f..e7bfee34e76a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -34,10 +34,12 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.toOffset import androidx.compose.ui.unit.toSize @@ -235,6 +237,7 @@ fun Modifier.dragContainer( } /** Wrap LazyGrid item with additional modifier needed for drag and drop. */ +@OptIn(ExperimentalComposeUiApi::class) @ExperimentalFoundationApi @Composable fun LazyGridItemScope.DraggableItem( @@ -269,7 +272,11 @@ fun LazyGridItemScope.DraggableItem( Box(modifier) { Box(draggingModifier) { content(dragging) } AnimatedVisibility( - modifier = Modifier.matchParentSize(), + modifier = + Modifier.matchParentSize() + // Do not consume motion events in the highlighted item and pass them down to + // the content. + .pointerInteropFilter { false }, visible = (dragging || selected) && !dragDropState.isDraggingToRemove, enter = fadeIn(), exit = fadeOut() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 182798e097d3..53aee5da0793 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2520,18 +2520,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, String message = ""; switch (msg.what) { case SHOW: - // There is a potential race condition when SysUI starts up. CentralSurfaces - // must invoke #registerCentralSurfaces on this class before any messages can be - // processed. If this happens, repost the message with a small delay and try - // again. - if (mCentralSurfaces == null) { - message = "DELAYING SHOW"; - Message newMsg = mHandler.obtainMessage(SHOW, (Bundle) msg.obj); - mHandler.sendMessageDelayed(newMsg, 100); - } else { - message = "SHOW"; - handleShow((Bundle) msg.obj); - } + message = "SHOW"; + handleShow((Bundle) msg.obj); break; case HIDE: message = "HIDE"; @@ -2618,18 +2608,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Trace.endSection(); break; case SYSTEM_READY: - // There is a potential race condition when SysUI starts up. CentralSurfaces - // must invoke #registerCentralSurfaces on this class before any messages can be - // processed. If this happens, repost the message with a small delay and try - // again. - if (mCentralSurfaces == null) { - message = "DELAYING SYSTEM_READY"; - Message newMsg = mHandler.obtainMessage(SYSTEM_READY); - mHandler.sendMessageDelayed(newMsg, 100); - } else { - message = "SYSTEM_READY"; - handleSystemReady(); - } + message = "SYSTEM_READY"; + handleSystemReady(); break; } Log.d(TAG, "KeyguardViewMediator queue processing message: " + message); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java index 80c3551b7de0..321b6084831e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java @@ -71,6 +71,15 @@ public class RemoteInputNotificationRebuilder { @NonNull public StatusBarNotification rebuildForCanceledSmartReplies( NotificationEntry entry) { + return rebuildWithExistingReplies(entry); + } + + /** + * Rebuilds to include any previously-added remote input replies. + * For when the app cancels a notification that has already been lifetime extended. + */ + @NonNull + public StatusBarNotification rebuildWithExistingReplies(NotificationEntry entry) { return rebuildWithRemoteInputInserted(entry, null /* remoteInputText */, false /* showSpinner */, null /* mimeType */, null /* uri */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt index 28fff1519032..fe59d732cceb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt @@ -127,6 +127,15 @@ class RemoteInputCoordinator @Inject constructor( mSmartReplyController.stopSending(entry) mNotifUpdater.onInternalNotificationUpdate(newSbn, "Extending lifetime of notification with smart reply") + } else { + // The app may have re-cancelled a notification after it had already + // been lifetime extended. + // Rebuild the notification with the replies it already had to ensure + // those replies continue to be displayed. + val newSbn = mRebuilder.rebuildWithExistingReplies(entry) + mNotifUpdater.onInternalNotificationUpdate(newSbn, + "Extending lifetime of notification that has already been " + + "lifetime extended.") } } else { // Notifications updated without FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY 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 f76de04c0c18..710cdcd5fcde 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -836,7 +836,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy; mLightRevealScrim = lightRevealScrim; - // Based on teamfood flag, turn predictive back dispatch on at runtime. if (predictiveBackSysui()) { mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true); } @@ -3060,6 +3059,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { public void onConfigChanged(Configuration newConfig) { updateResources(); updateDisplaySize(); // populates mDisplayMetrics + if (predictiveBackSysui()) { + mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true); + } if (DEBUG) { Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 272b48876a37..11ec417fc5f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -306,28 +306,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) - public void testRaceCondition_doNotRegisterCentralSurfacesImmediately() { - create(false); - - // GIVEN central surfaces is not registered with KeyguardViewMediator, but a call to enable - // keyguard comes in - mViewMediator.onSystemReady(); - mViewMediator.setKeyguardEnabled(true); - TestableLooper.get(this).processAllMessages(); - - // If this step has been reached, then system ui has not crashed. Now register - // CentralSurfaces - assertFalse(mViewMediator.isShowingAndNotOccluded()); - register(); - TestableLooper.get(this).moveTimeForward(100); - TestableLooper.get(this).processAllMessages(); - - // THEN keyguard is shown - assertTrue(mViewMediator.isShowingAndNotOccluded()); - } - - @Test - @TestableLooper.RunWithLooper(setAsMainLooper = true) public void onLockdown_showKeyguard_evenIfKeyguardIsNotEnabledExternally() { // GIVEN keyguard is not enabled and isn't showing mViewMediator.onSystemReady(); @@ -1206,11 +1184,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { } private void createAndStartViewMediator(boolean orderUnlockAndWake) { - create(orderUnlockAndWake); - register(); - } - - private void create(boolean orderUnlockAndWake) { mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_orderUnlockAndWake, orderUnlockAndWake); @@ -1262,9 +1235,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mKeyguardInteractor, mock(WindowManagerOcclusionManager.class)); mViewMediator.start(); - } - private void register() { mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt index 85b8b03a1b46..d3df48e9ef02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt @@ -98,6 +98,7 @@ class RemoteInputCoordinatorTest : SysuiTestCase() { `when`(rebuilder.rebuildForCanceledSmartReplies(any())).thenReturn(sbn) `when`(rebuilder.rebuildForRemoteInputReply(any())).thenReturn(sbn) `when`(rebuilder.rebuildForSendingSmartReply(any(), any())).thenReturn(sbn) + `when`(rebuilder.rebuildWithExistingReplies(any())).thenReturn(sbn) } val remoteInputActiveExtender get() = coordinator.mRemoteInputActiveExtender @@ -208,13 +209,30 @@ class RemoteInputCoordinatorTest : SysuiTestCase() { it.onEntryUpdated(entry, true) } - verify(rebuilder, times(1)).rebuildForCanceledSmartReplies(entry) verify(smartReplyController, times(1)).stopSending(entry) } @Test @EnableFlags(FLAG_LIFETIME_EXTENSION_REFACTOR) + fun testRepeatedUpdateTriggersRebuild() { + // Create notification with LIFETIME_EXTENDED_BY_DIRECT_REPLY flag. + val entry = NotificationEntryBuilder() + .setId(3) + .setTag("entry") + .setFlag(mContext, Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true) + .build() + `when`(remoteInputManager.shouldKeepForRemoteInputHistory(entry)).thenReturn(false) + `when`(remoteInputManager.shouldKeepForSmartReplyHistory(entry)).thenReturn(false) + collectionListeners.forEach { + it.onEntryUpdated(entry, true) + } + + verify(rebuilder, times(1)).rebuildWithExistingReplies(entry) + } + + @Test + @EnableFlags(FLAG_LIFETIME_EXTENSION_REFACTOR) fun testLifetimeExtensionListenerClearsRemoteInputs() { // Create notification with LIFETIME_EXTENDED_BY_DIRECT_REPLY flag. val entry = NotificationEntryBuilder() diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java index b28bc1ffb611..16a99333796c 100644 --- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java +++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java @@ -29,6 +29,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.app.ActivityOptions; import android.app.admin.DevicePolicyManagerInternal; +import android.app.contextualsearch.CallbackToken; import android.app.contextualsearch.ContextualSearchManager; import android.app.contextualsearch.ContextualSearchState; import android.app.contextualsearch.IContextualSearchCallback; @@ -164,7 +165,7 @@ public class ContextualSearchManagerService extends SystemService { } } - private Intent getContextualSearchIntent(int entrypoint, IBinder mToken) { + private Intent getContextualSearchIntent(int entrypoint, CallbackToken mToken) { final Intent launchIntent = getResolvedLaunchIntent(); if (launchIntent == null) { return null; @@ -256,14 +257,14 @@ public class ContextualSearchManagerService extends SystemService { } private class ContextualSearchManagerStub extends IContextualSearchManager.Stub { - private @Nullable IBinder mToken; + private @Nullable CallbackToken mToken; @Override public void startContextualSearch(int entrypoint) { synchronized (this) { if (DEBUG_USER) Log.d(TAG, "startContextualSearch"); enforcePermission("startContextualSearch"); - mToken = new Binder(); + mToken = new CallbackToken(); // We get the launch intent with the system server's identity because the system // server has READ_FRAME_BUFFER permission to get the screenshot and because only // the system server can invoke non-exported activities. @@ -284,7 +285,7 @@ public class ContextualSearchManagerService extends SystemService { if (DEBUG_USER) { Log.i(TAG, "getContextualSearchState token: " + token + ", callback: " + callback); } - if (mToken == null || !mToken.equals(token)) { + if (mToken == null || !mToken.getToken().equals(token)) { if (DEBUG_USER) { Log.e(TAG, "getContextualSearchState: invalid token, returning error"); } |