diff options
68 files changed, 667 insertions, 199 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 2fd2e33bbc37..9b290c6a4760 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -12,8 +12,6 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp services/incremental/ [Hook Scripts] -checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} - strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT} hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT} diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index fe270a480d83..fed9c43faa38 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -68,6 +68,8 @@ public final class Telecom extends BaseCommand { private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account"; private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer"; private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer"; + private static final String COMMAND_STOP_BLOCK_SUPPRESSION = "stop-block-suppression"; + /** * Change the system dialer package name if a package name was specified, * Example: adb shell telecom set-system-dialer <PACKAGE> @@ -115,6 +117,8 @@ public final class Telecom extends BaseCommand { + "usage: telecom set-sim-count <COUNT>\n" + "usage: telecom get-sim-config\n" + "usage: telecom get-max-phones\n" + + "usage: telecom stop-block-suppression: Stop suppressing the blocked number" + + " provider after a call to emergency services.\n" + "usage: telecom set-emer-phone-account-filter <PACKAGE>\n" + "\n" + "telecom set-phone-account-enabled: Enables the given phone account, if it has" @@ -207,6 +211,9 @@ public final class Telecom extends BaseCommand { case COMMAND_UNREGISTER_PHONE_ACCOUNT: runUnregisterPhoneAccount(); break; + case COMMAND_STOP_BLOCK_SUPPRESSION: + runStopBlockSuppression(); + break; case COMMAND_SET_DEFAULT_DIALER: runSetDefaultDialer(); break; @@ -324,8 +331,13 @@ public final class Telecom extends BaseCommand { System.out.println("Success - " + handle + " unregistered."); } + private void runStopBlockSuppression() throws RemoteException { + mTelecomService.stopBlockSuppression(); + } + private void runSetDefaultDialer() throws RemoteException { - final String packageName = nextArgRequired(); + String packageName = nextArg(); + if ("default".equals(packageName)) packageName = null; mTelecomService.setTestDefaultDialer(packageName); System.out.println("Success - " + packageName + " set as override default dialer."); } diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index cfe0aff05d4a..e7b3e14bfda7 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -502,7 +502,7 @@ public final class ApplicationExitInfo implements Parcelable { * Return the defining kernel user identifier, maybe different from {@link #getRealUid} and * {@link #getPackageUid}, if an external service has the * {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set - * to <code>true<code> and was bound with the flag + * to <code>true</code> and was bound with the flag * {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here will * be the kernel user identifier of the external service provider. */ diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index a828aac78ded..505b498e3cf6 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1900,26 +1900,31 @@ class ContextImpl extends Context { @Override public Object getSystemService(String name) { - // We may override this API from outer context. - final boolean isUiContext = isUiContext() || getOuterContext().isUiContext(); - // Check incorrect Context usage. - if (isUiComponent(name) && !isUiContext && vmIncorrectContextUseEnabled()) { - final String errorMessage = "Tried to access visual service " - + SystemServiceRegistry.getSystemServiceClassName(name) - + " from a non-visual Context:" + getOuterContext(); - final String message = "Visual services, such as WindowManager, WallpaperService or " - + "LayoutInflater should be accessed from Activity or other visual Context. " - + "Use an Activity or a Context created with " - + "Context#createWindowContext(int, Bundle), which are adjusted to the " - + "configuration and visual bounds of an area on screen."; - final Exception exception = new IllegalAccessException(errorMessage); - StrictMode.onIncorrectContextUsed(message, exception); - Log.e(TAG, errorMessage + message, exception); + if (vmIncorrectContextUseEnabled()) { + // We may override this API from outer context. + final boolean isUiContext = isUiContext() || isOuterUiContext(); + // Check incorrect Context usage. + if (isUiComponent(name) && !isUiContext) { + final String errorMessage = "Tried to access visual service " + + SystemServiceRegistry.getSystemServiceClassName(name) + + " from a non-visual Context:" + getOuterContext(); + final String message = "Visual services, such as WindowManager, WallpaperService " + + "or LayoutInflater should be accessed from Activity or other visual " + + "Context. Use an Activity or a Context created with " + + "Context#createWindowContext(int, Bundle), which are adjusted to " + + "the configuration and visual bounds of an area on screen."; + final Exception exception = new IllegalAccessException(errorMessage); + StrictMode.onIncorrectContextUsed(message, exception); + Log.e(TAG, errorMessage + " " + message, exception); + } } - return SystemServiceRegistry.getSystemService(this, name); } + private boolean isOuterUiContext() { + return getOuterContext() != null && getOuterContext().isUiContext(); + } + @Override public String getSystemServiceName(Class<?> serviceClass) { return SystemServiceRegistry.getSystemServiceName(serviceClass); @@ -2371,7 +2376,7 @@ class ContextImpl extends Context { context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId, overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(), mResources.getLoaders())); - context.mIsUiContext = isUiContext() || getOuterContext().isUiContext(); + context.mIsUiContext = isUiContext() || isOuterUiContext(); return context; } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 5dc41e483640..89abfc95d634 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1151,6 +1151,9 @@ public class ContextWrapper extends Context { */ @Override public boolean isUiContext() { + if (mBase == null) { + return false; + } return mBase.isUiContext(); } } diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 79da1f6ab282..ee9bd3d259fb 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -1168,7 +1168,12 @@ public class IntentFilter implements Parcelable { public int match(Uri data, boolean wildcardSupported) { String host = data.getHost(); if (host == null) { - return NO_MATCH_DATA; + if (wildcardSupported && mWild) { + // special case, if no host is provided, but the Authority is wildcard, match + return MATCH_CATEGORY_HOST; + } else { + return NO_MATCH_DATA; + } } if (false) Log.v("IntentFilter", "Match host " + host + ": " + mHost); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c577d0e896b0..53e75d4548ba 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4666,8 +4666,7 @@ public abstract class PackageManager { * Marks an application exempt from having its permissions be automatically revoked when * the app is unused for an extended period of time. * - * Only the installer on record that installed the given package, or a holder of - * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this. + * Only the installer on record that installed the given package is allowed to call this. * * Packages start in whitelisted state, and it is the installer's responsibility to * un-whitelist the packages it installs, unless auto-revoking permissions from that package diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index bc9c71e7a68e..ab0ed51fb909 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -1510,7 +1510,7 @@ public class ParsingPackageUtils { Uri data = null; String dataType = null; - String host = IntentFilter.WILDCARD; + String host = null; final int numActions = intentInfo.countActions(); final int numSchemes = intentInfo.countDataSchemes(); final int numTypes = intentInfo.countDataTypes(); diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java index 509cbe09df69..90d0ff0a5026 100644 --- a/core/java/android/inputmethodservice/InlineSuggestionSession.java +++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java @@ -149,6 +149,11 @@ class InlineSuggestionSession { */ @MainThread void invalidate() { + try { + mCallback.onInlineSuggestionsSessionInvalidated(); + } catch (RemoteException e) { + Log.w(TAG, "onInlineSuggestionsSessionInvalidated() remote exception:" + e); + } if (mResponseCallback != null) { consumeInlineSuggestionsResponse(EMPTY_RESPONSE); mResponseCallback.invalidate(); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 5647bf90d2fb..c5a11abe1136 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -605,9 +605,6 @@ public class InputMethodService extends AbstractInputMethodService { if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding + " ic=" + mInputConnection); // Unbind input is per process per display. - // TODO(b/150902448): free-up IME surface when target is changing. - // e.g. DisplayContent#setInputMethodTarget() - removeImeSurface(); onUnbindInput(); mInputBinding = null; mInputConnection = null; diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 0e10c42e61db..0eb3c1e8ad01 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -38,7 +38,9 @@ import java.util.Arrays; * Representation of a MAC address. * * This class only supports 48 bits long addresses and does not support 64 bits long addresses. - * Instances of this class are immutable. + * Instances of this class are immutable. This class provides implementations of hashCode() + * and equals() that make it suitable for use as keys in standard implementations of + * {@link java.util.Map}. */ public final class MacAddress implements Parcelable { @@ -122,12 +124,22 @@ public final class MacAddress implements Parcelable { } /** + * Convert this MacAddress to a byte array. + * + * The returned array is in network order. For example, if this MacAddress is 1:2:3:4:5:6, + * the returned array is [1, 2, 3, 4, 5, 6]. + * * @return a byte array representation of this MacAddress. */ public @NonNull byte[] toByteArray() { return byteAddrFromLongAddr(mAddr); } + /** + * Returns a human-readable representation of this MacAddress. + * The exact format is implementation-dependent and should not be assumed to have any + * particular format. + */ @Override public @NonNull String toString() { return stringAddrFromLongAddr(mAddr); diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index ef9d990168d2..c1998c6009cf 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -119,11 +119,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching // this code here means that we now got control, so we can start the animation immediately. // If client window is trying to control IME and IME is already visible, it is immediate. - if (fromIme || mState.getSource(getType()).isVisible()) { + if (fromIme || mState.getSource(getType()).isVisible() && getControl() != null) { return ShowResult.SHOW_IMMEDIATELY; } - return getImm().requestImeShow(null /* resultReceiver */) + return getImm().requestImeShow(mController.getHost().getWindowToken()) ? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED; } @@ -132,12 +132,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { */ @Override void notifyHidden() { - getImm().notifyImeHidden(); + getImm().notifyImeHidden(mController.getHost().getWindowToken()); } @Override public void removeSurface() { - getImm().removeImeSurface(); + final IBinder window = mController.getHost().getWindowToken(); + if (window != null) { + getImm().removeImeSurface(window); + } } @Override @@ -146,6 +149,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { super.setControl(control, showTypes, hideTypes); if (control == null && !mIsRequestedVisibleAwaitingControl) { hide(); + removeSurface(); } } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index c6be91fa1bf5..a679b3740fd9 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -737,7 +737,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } - final boolean hasControl = mTmpControlArray.size() > 0; + boolean requestedStateStale = false; final int[] showTypes = new int[1]; final int[] hideTypes = new int[1]; @@ -754,9 +754,26 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // Ensure to create source consumers if not available yet. for (int i = mTmpControlArray.size() - 1; i >= 0; i--) { final InsetsSourceControl control = mTmpControlArray.valueAt(i); - InsetsSourceConsumer consumer = getSourceConsumer(control.getType()); + final @InternalInsetsType int type = control.getType(); + final InsetsSourceConsumer consumer = getSourceConsumer(type); consumer.setControl(control, showTypes, hideTypes); + if (!requestedStateStale) { + final boolean requestedVisible = consumer.isRequestedVisible(); + + // We might have changed our requested visibilities while we don't have the control, + // so we need to update our requested state once we have control. Otherwise, our + // requested state at the server side might be incorrect. + final boolean requestedVisibilityChanged = + requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type); + + // The IME client visibility will be reset by insets source provider while updating + // control, so if IME is requested visible, we need to send the request to server. + final boolean imeRequestedVisible = type == ITYPE_IME && requestedVisible; + + requestedStateStale = requestedVisibilityChanged || imeRequestedVisible; + } + } mTmpControlArray.clear(); @@ -772,10 +789,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (hideTypes[0] != 0) { applyAnimation(hideTypes[0], false /* show */, false /* fromIme */); } - if (hasControl && mRequestedState.hasSources()) { - // We might have changed our requested visibilities while we don't have the control, - // so we need to update our requested state once we have control. Otherwise, our - // requested state at the server side might be incorrect. + if (requestedStateStale) { updateRequestedState(); } } diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 40e6f4b2fce8..700dc66fab55 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -153,6 +153,9 @@ public class InsetsSourceConsumer { if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) { applyHiddenToControl(); } + if (!requestedVisible && !mIsAnimationPending) { + removeSurface(); + } } } if (lastControl != null) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 814293a8d8b7..fefe564787ca 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2731,7 +2731,6 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer.isEnabled()) { mAttachInfo.mThreadedRenderer.destroy(); } - notifySurfaceDestroyed(); } else if ((surfaceReplaced || surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged) && mSurfaceHolder == null @@ -2971,6 +2970,10 @@ public final class ViewRootImpl implements ViewParent, } } + if (surfaceDestroyed) { + notifySurfaceDestroyed(); + } + if (triggerGlobalLayoutListener) { mAttachInfo.mRecomputeGlobalAttributes = false; mAttachInfo.mTreeObserver.dispatchOnGlobalLayout(); @@ -9392,6 +9395,11 @@ public final class ViewRootImpl implements ViewParent, return mInputEventReceiver.getToken(); } + @NonNull + public IBinder getWindowToken() { + return mAttachInfo.mWindowToken; + } + /** * Class for managing the accessibility interaction connection * based on the global accessibility state. diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index aedb59bfee42..3be0a4d39a56 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2109,28 +2109,36 @@ public final class InputMethodManager { /** * Call showSoftInput with currently focused view. - * @return {@code true} if IME can be shown. + * + * @param windowToken the window from which this request originates. If this doesn't match the + * currently served view, the request is ignored and returns {@code false}. + * + * @return {@code true} if IME can (eventually) be shown, {@code false} otherwise. * @hide */ - public boolean requestImeShow(ResultReceiver resultReceiver) { + public boolean requestImeShow(IBinder windowToken) { synchronized (mH) { final View servedView = getServedViewLocked(); - if (servedView == null) { + if (servedView == null || servedView.getWindowToken() != windowToken) { return false; } - showSoftInput(servedView, 0 /* flags */, resultReceiver); + showSoftInput(servedView, 0 /* flags */, null /* resultReceiver */); return true; } } /** * Notify IME directly that it is no longer visible. + * + * @param windowToken the window from which this request originates. If this doesn't match the + * currently served view, the request is ignored. * @hide */ - public void notifyImeHidden() { + public void notifyImeHidden(IBinder windowToken) { synchronized (mH) { try { - if (mCurMethod != null) { + if (mCurMethod != null && mCurRootView != null + && mCurRootView.getWindowToken() == windowToken) { mCurMethod.notifyImeHidden(); } } catch (RemoteException re) { @@ -2140,15 +2148,15 @@ public final class InputMethodManager { /** * Notify IME directly to remove surface as it is no longer visible. + * @param windowToken The client window token that requests the IME to remove its surface. * @hide */ - public void removeImeSurface() { + public void removeImeSurface(IBinder windowToken) { synchronized (mH) { try { - if (mCurMethod != null) { - mCurMethod.removeImeSurface(); - } - } catch (RemoteException re) { + mService.removeImeSurfaceFromWindow(windowToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } } diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java index ff3ac0732aa2..9eb63087a66e 100644 --- a/core/java/android/widget/EditorTouchState.java +++ b/core/java/android/widget/EditorTouchState.java @@ -174,12 +174,9 @@ public class EditorTouchState { int touchSlop = config.getScaledTouchSlop(); mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop; if (mMovedEnoughForDrag) { - // If the direction of the swipe motion is within 30 degrees of vertical, it is - // considered a vertical drag. We don't actually have to compute the angle to - // implement the check though. When the angle is exactly 30 degrees from - // vertical, 2*deltaX = distance. When the angle is less than 30 degrees from - // vertical, 2*deltaX < distance. - mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared; + // If the direction of the swipe motion is within 45 degrees of vertical, it is + // considered a vertical drag. + mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY); } } } else if (action == MotionEvent.ACTION_CANCEL) { diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index c5729b05c587..046981cf2e8f 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -21,8 +21,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COM import static android.view.View.SYSTEM_UI_LAYOUT_FLAGS; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static android.view.WindowInsets.Type.ime; -import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; @@ -34,8 +32,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import android.annotation.NonNull; import android.annotation.Nullable; @@ -146,17 +142,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if ((view.getWindowSystemUiVisibility() & SYSTEM_UI_LAYOUT_FLAGS) != 0) { return new Pair<>(Insets.NONE, insets); } - - boolean includeIme = (view.getViewRootImpl().mWindowAttributes.softInputMode - & SOFT_INPUT_MASK_ADJUST) - == SOFT_INPUT_ADJUST_RESIZE; - Insets insetsToApply; - if (ViewRootImpl.sNewInsetsMode == 0) { - insetsToApply = insets.getSystemWindowInsets(); - } else { - insetsToApply = insets.getInsets(systemBars() | (includeIme ? ime() : 0)); - } - insets = insets.inset(insetsToApply); + Insets insetsToApply = insets.getSystemWindowInsets(); return new Pair<>(insetsToApply, insets.inset(insetsToApply).consumeSystemWindowInsets()); }; diff --git a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl index cf1220c08467..03948a92bcab 100644 --- a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl +++ b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl @@ -58,4 +58,7 @@ oneway interface IInlineSuggestionsRequestCallback { // #onFinishInput()} is called on the field specified by the earlier // {@link #onInputMethodStartInput(AutofillId)}. void onInputMethodFinishInput(); + + // Indicates that the current IME changes inline suggestion session. + void onInlineSuggestionsSessionInvalidated(); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 8ec51b89d240..a1cbd3fcae79 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -73,5 +73,8 @@ interface IInputMethodManager { in float[] matrixValues); oneway void reportPerceptible(in IBinder windowToken, boolean perceptible); + /** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */ void removeImeSurface(); + /** Remove the IME surface. Requires passing the currently focused window. */ + void removeImeSurfaceFromWindow(in IBinder windowToken); } diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 4640e4b699d6..5e245de3376e 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1149,7 +1149,7 @@ <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Capturar imagen con %1$s"</string> <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Capturar imagen"</string> <string name="alwaysUse" msgid="3153558199076112903">"Usar siempre para esta acción"</string> - <string name="use_a_different_app" msgid="4987790276170972776">"Utiliza otra aplicación"</string> + <string name="use_a_different_app" msgid="4987790276170972776">"Usar otra aplicación"</string> <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Para borrar los valores predeterminados, accede a Ajustes del sistema > Aplicaciones > Descargadas."</string> <string name="chooseActivity" msgid="8563390197659779956">"Selecciona una acción"</string> <string name="chooseUsbActivity" msgid="2096269989990986612">"Elegir una aplicación para el dispositivo USB"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index c26cc66dbab4..cf08d242e635 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -1926,7 +1926,7 @@ <string name="time_picker_header_text" msgid="9073802285051516688">"Ezarri ordua"</string> <string name="time_picker_input_error" msgid="8386271930742451034">"Idatzi balio duen ordu bat"</string> <string name="time_picker_prompt_label" msgid="303588544656363889">"Idatzi ordua"</string> - <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Aldatu testu modura ordua zehazteko."</string> + <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Ordua idazteko, aldatu testua idazteko metodora."</string> <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Aldatu erloju modura ordua zehazteko."</string> <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Betetze automatikoaren aukerak"</string> <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Gorde betetze automatikoarekin erabiltzeko"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 77a1fec768d7..1732d0846961 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -854,7 +854,7 @@ <string name="emergency_calls_only" msgid="3057351206678279851">"Só chamadas de emerxencia"</string> <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Bloqueada pola rede"</string> <string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"A tarxeta SIM está bloqueada con código PUK."</string> - <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta a guía do usuario ou ponte en contacto co servizo de asistencia ao cliente."</string> + <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta a guía para usuarios ou ponte en contacto co servizo de asistencia ao cliente."</string> <string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"A tarxeta SIM está bloqueada."</string> <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"Desbloqueando tarxeta SIM…"</string> <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index f5686c2ec12d..1604bea1e763 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1149,7 +1149,7 @@ <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"「%1$s」を使用して画像をキャプチャ"</string> <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"画像をキャプチャ"</string> <string name="alwaysUse" msgid="3153558199076112903">"常にこの操作で使用する"</string> - <string name="use_a_different_app" msgid="4987790276170972776">"別のアプリの使用"</string> + <string name="use_a_different_app" msgid="4987790276170972776">"別のアプリを使用"</string> <string name="clearDefaultHintMsg" msgid="1325866337702524936">"[システム設定]>[アプリ]>[ダウンロード済み]でデフォルト設定をクリアします。"</string> <string name="chooseActivity" msgid="8563390197659779956">"操作の選択"</string> <string name="chooseUsbActivity" msgid="2096269989990986612">"USBデバイス用アプリを選択"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 28409e019a59..c55946e88ae9 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1547,7 +1547,7 @@ <string name="sending" msgid="206925243621664438">"Жіберілуде..."</string> <string name="launchBrowserDefault" msgid="6328349989932924119">"Браузер қосылсын ба?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"Қоңырауды қабылдау?"</string> - <string name="activity_resolver_use_always" msgid="5575222334666843269">"Үнемі"</string> + <string name="activity_resolver_use_always" msgid="5575222334666843269">"Әрқашан"</string> <string name="activity_resolver_use_once" msgid="948462794469672658">"Бір рет қана"</string> <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s жұмыс профилін қолдамайды"</string> <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Планшет"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index a86636218292..5908475eec04 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -536,7 +536,7 @@ <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Колдонмого сүрөт жыйнагыңызды өзгөртүүгө мүмкүнчүлүк берет."</string> <string name="permlab_mediaLocation" msgid="7368098373378598066">"медиа жыйнагыңыз сакталган жерлерди окуу"</string> <string name="permdesc_mediaLocation" msgid="597912899423578138">"Колдонмого медиа жыйнагыңыз сакталган жерлерди окууга мүмкүнчүлүк берет."</string> - <string name="biometric_dialog_default_title" msgid="55026799173208210">"Сиз экениңизди ырастаңыз"</string> + <string name="biometric_dialog_default_title" msgid="55026799173208210">"Өзүңүздү ырастаңыз"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалык аппарат жеткиликсиз"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аныктыгын текшерүү жокко чыгарылды"</string> <string name="biometric_not_recognized" msgid="5106687642694635888">"Таанылган жок"</string> @@ -1547,7 +1547,7 @@ <string name="sending" msgid="206925243621664438">"Жөнөтүлүүдө…"</string> <string name="launchBrowserDefault" msgid="6328349989932924119">"Серепчи иштетилсинби?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"Чалуу кабыл алынсынбы?"</string> - <string name="activity_resolver_use_always" msgid="5575222334666843269">"Дайыма"</string> + <string name="activity_resolver_use_always" msgid="5575222334666843269">"Ар дайым"</string> <string name="activity_resolver_use_once" msgid="948462794469672658">"Бир жолу гана"</string> <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s жумуш профилин колдоого албайт"</string> <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Планшет"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 05337584d774..e07a81d5892a 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1090,7 +1090,7 @@ <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll" msgid="1532369154488982046">"Hammasini belgilash"</string> - <string name="cut" msgid="2561199725874745819">"Kesish"</string> + <string name="cut" msgid="2561199725874745819">"Kesib olish"</string> <string name="copy" msgid="5472512047143665218">"Nusxa olish"</string> <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Vaqtinchalik xotiraga nusxalab bo‘lmadi"</string> <string name="paste" msgid="461843306215520225">"Joylash"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 69358d44d737..72dd4581e8fe 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1792,8 +1792,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"已由你的管理員更新"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由你的管理員刪除"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string> - <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n"<annotation id="url">"瞭解詳情"</annotation></string> - <string name="battery_saver_description" msgid="8587408568232177204">"為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞"</string> + <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"為了延長電池續航力,省電模式會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n"<annotation id="url">"瞭解詳情"</annotation></string> + <string name="battery_saver_description" msgid="8587408568232177204">"為了延長電池續航力,省電模式會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞"</string> <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你目前使用的應用程式可以存取資料,但存取頻率可能不如平時高。舉例來說,圖片可能不會自動顯示,在你輕觸後才會顯示。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟數據節省模式嗎?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string> @@ -1999,9 +1999,9 @@ <string name="notification_appops_overlay_active" msgid="5571732753262836481">"顯示在畫面上的其他應用程式上層"</string> <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"日常安排模式資訊通知"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"電池電力可能會在你平常的充電時間前耗盡"</string> - <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用節約耗電量模式以延長電池續航力"</string> - <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"節約耗電量"</string> - <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"節約耗電量模式已關閉"</string> + <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用省電模式以延長電池續航力"</string> + <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"省電模式"</string> + <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"省電模式已關閉"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"手機電力充足,各項功能不再受到限制。"</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"平板電腦電力充足,各項功能不再受到限制。"</string> <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"裝置電力充足,各項功能不再受到限制。"</string> diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java index 17d1389a6602..777f4a3e03a8 100644 --- a/core/tests/coretests/src/android/content/ContextTest.java +++ b/core/tests/coretests/src/android/content/ContextTest.java @@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.app.ActivityThread; @@ -180,4 +181,26 @@ public class ContextTest { VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); return virtualDisplay.getDisplay(); } + + @Test + public void testIsUiContext_ContextWrapper() { + ContextWrapper wrapper = new ContextWrapper(null /* base */); + + assertFalse(wrapper.isUiContext()); + + wrapper = new ContextWrapper(new TestUiContext()); + + assertTrue(wrapper.isUiContext()); + } + + private static class TestUiContext extends ContextWrapper { + TestUiContext() { + super(null /* base */); + } + + @Override + public boolean isUiContext() { + return true; + } + } } diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 702f2fa65487..c36f1067149e 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -691,18 +691,57 @@ public class InsetsControllerTest { @Test public void testRequestedState() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + + // The modified state can be controlled when we have control. mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); mController.hide(statusBars()); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); - mController.onControlsChanged(new InsetsSourceControl[0]); + + // The modified state won't be changed while losing control. + mController.onControlsChanged(null /* activeControls */); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); + + // The modified state won't be changed while state changed while we don't have control. InsetsState newState = new InsetsState(mController.getState(), true /* copySource */); mController.onStateChanged(newState); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); + + // The modified state won't be changed while controlling an insets without having the + // control. mController.show(statusBars()); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); + + // The modified state can be updated while gaining control. mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); assertTrue(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); + + // The modified state can still be updated if the local state and the requested state + // are the same. + mController.onControlsChanged(null /* activeControls */); + mController.hide(statusBars()); + newState = new InsetsState(mController.getState(), true /* copySource */); + newState.getSource(ITYPE_STATUS_BAR).setVisible(false); + mController.onStateChanged(newState); + mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); + assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); + + // The modified state will always be updated while receiving IME control if IME is + // requested visible. + mController.getSourceConsumer(ITYPE_IME).show(false /* fromIme */); + newState = new InsetsState(mController.getState(), true /* copySource */); + newState.getSource(ITYPE_IME).setVisible(true); + newState.getSource(ITYPE_IME).setFrame(1, 2, 3, 4); + mController.onStateChanged(newState); + mController.onControlsChanged(createSingletonControl(ITYPE_IME)); + assertEquals(newState.getSource(ITYPE_IME), + mTestHost.getModifiedState().peekSource(ITYPE_IME)); + newState = new InsetsState(mController.getState(), true /* copySource */); + newState.getSource(ITYPE_IME).setVisible(true); + newState.getSource(ITYPE_IME).setFrame(5, 6, 7, 8); + mController.onStateChanged(newState); + mController.onControlsChanged(createSingletonControl(ITYPE_IME)); + assertEquals(newState.getSource(ITYPE_IME), + mTestHost.getModifiedState().peekSource(ITYPE_IME)); }); } diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 1b3272572db0..7efd616c5607 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -71,6 +71,9 @@ public class InsetsSourceConsumerTest { private SurfaceControl mLeash; @Mock Transaction mMockTransaction; private InsetsSource mSpyInsetsSource; + private boolean mRemoveSurfaceCalled = false; + private InsetsController mController; + private InsetsState mState; @Before public void setup() { @@ -89,13 +92,19 @@ public class InsetsSourceConsumerTest { } catch (BadTokenException e) { // activity isn't running, lets ignore BadTokenException. } - InsetsState state = new InsetsState(); + mState = new InsetsState(); mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR)); - state.addSource(mSpyInsetsSource); - - mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, state, - () -> mMockTransaction, - new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl))); + mState.addSource(mSpyInsetsSource); + + mController = new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl)); + mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState, + () -> mMockTransaction, mController) { + @Override + public void removeSurface() { + super.removeSurface(); + mRemoveSurfaceCalled = true; + } + }; }); instrumentation.waitForIdleSync(); @@ -171,6 +180,25 @@ public class InsetsSourceConsumerTest { mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()), new int[1], hideTypes); assertEquals(statusBars(), hideTypes[0]); + assertFalse(mRemoveSurfaceCalled); + }); + } + + @Test + public void testRestore_noAnimation() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + mConsumer.hide(); + mController.onStateChanged(mState); + mConsumer.setControl(null, new int[1], new int[1]); + reset(mMockTransaction); + verifyZeroInteractions(mMockTransaction); + mRemoveSurfaceCalled = false; + int[] hideTypes = new int[1]; + mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()), + new int[1], hideTypes); + assertTrue(mRemoveSurfaceCalled); + assertEquals(0, hideTypes[0]); }); + } } diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index 89cc6e743752..838f60069f27 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java @@ -145,7 +145,7 @@ public class EditorCursorDragTest { // Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to // the handle as the touch moves downwards (and because we have some slop to avoid jumping // across lines), the cursor position will end up higher than the finger position. - onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3"))); + onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2"))); onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1"))); // Swipe right-down along a very steep diagonal path. This should not drag the cursor. @@ -181,7 +181,7 @@ public class EditorCursorDragTest { // Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to // the handle as the touch moves downwards (and because we have some slop to avoid jumping // across lines), the cursor position will end up higher than the finger position. - onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3"))); + onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2"))); onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1"))); // Swipe right-down along a very steep diagonal path. This should not drag the cursor. diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java index ec75e40f1334..35fd4bd7dc14 100644 --- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java +++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java @@ -326,9 +326,9 @@ public class EditorTouchStateTest { mTouchState.update(event1, mConfig); assertSingleTap(mTouchState, 0f, 0f, 0, 0); - // Simulate an ACTION_MOVE event that is > 30 deg from vertical. + // Simulate an ACTION_MOVE event that is > 45 deg from vertical. long event2Time = 1002; - MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 173f); + MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f); mTouchState.update(event2, mConfig); assertDrag(mTouchState, 0f, 0f, 0, 0, false); diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java index 278b57da0c28..e5ea4467517b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java @@ -41,7 +41,7 @@ public class CircleFramedDrawable extends Drawable { private final Bitmap mBitmap; private final int mSize; - private final Paint mPaint; + private Paint mIconPaint; private float mScale; private Rect mSrcRect; @@ -75,18 +75,18 @@ public class CircleFramedDrawable extends Drawable { canvas.drawColor(0, PorterDuff.Mode.CLEAR); // opaque circle matte - mPaint = new Paint(); - mPaint.setAntiAlias(true); - mPaint.setColor(Color.BLACK); - mPaint.setStyle(Paint.Style.FILL); - canvas.drawPath(fillPath, mPaint); + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setColor(Color.BLACK); + paint.setStyle(Paint.Style.FILL); + canvas.drawPath(fillPath, paint); // mask in the icon where the bitmap is opaque - mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); - canvas.drawBitmap(icon, cropRect, circleRect, mPaint); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + canvas.drawBitmap(icon, cropRect, circleRect, paint); // prepare paint for frame drawing - mPaint.setXfermode(null); + paint.setXfermode(null); mScale = 1f; @@ -100,7 +100,7 @@ public class CircleFramedDrawable extends Drawable { final float pad = (mSize - inside) / 2f; mDstRect.set(pad, pad, mSize - pad, mSize - pad); - canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null); + canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, mIconPaint); } public void setScale(float scale) { @@ -122,8 +122,12 @@ public class CircleFramedDrawable extends Drawable { @Override public void setColorFilter(ColorFilter cf) { + if (mIconPaint == null) { + mIconPaint = new Paint(); + } + mIconPaint.setColorFilter(cf); } - + @Override public int getIntrinsicWidth() { return mSize; diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml index 597644bf3295..4527c6c793d5 100644 --- a/packages/SystemUI/res/layout/qs_panel.xml +++ b/packages/SystemUI/res/layout/qs_panel.xml @@ -53,6 +53,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:elevation="4dp" + android:importantForAccessibility="no" android:layout_weight="1"> <com.android.systemui.qs.QSPanel android:id="@+id/quick_settings_panel" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java index 279a200949a5..1e505801c2c4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java @@ -26,6 +26,7 @@ import android.app.ActivityManager.TaskSnapshot; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.GraphicBuffer; +import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.util.Log; @@ -63,10 +64,12 @@ public class ThumbnailData { public ThumbnailData(TaskSnapshot snapshot) { final GraphicBuffer buffer = snapshot.getSnapshot(); - if (buffer != null && (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) { + if (buffer == null || (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) { // TODO(b/157562905): Workaround for a crash when we get a snapshot without this state - Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE"); - thumbnail = Bitmap.createBitmap(buffer.getWidth(), buffer.getHeight(), ARGB_8888); + Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: " + + buffer); + Point taskSize = snapshot.getTaskSize(); + thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888); thumbnail.eraseColor(Color.BLACK); } else { thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace()); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 127c5dd54d72..e12b7dd259a5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.content.res.Configuration import android.graphics.Color import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS +import android.util.Log import android.util.MathUtils import android.view.LayoutInflater import android.view.View @@ -25,6 +26,7 @@ import javax.inject.Inject import javax.inject.Provider import javax.inject.Singleton +private const val TAG = "MediaCarouselController" private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS) /** @@ -236,7 +238,9 @@ class MediaCarouselController @Inject constructor( val oldData = mediaPlayers[oldKey] if (oldData != null) { val oldData = mediaPlayers.remove(oldKey) - mediaPlayers.put(key, oldData!!) + mediaPlayers.put(key, oldData!!)?.let { + Log.wtf(TAG, "new key $key already exists when migrating from $oldKey") + } } var existingPlayer = mediaPlayers[key] if (existingPlayer == null) { @@ -271,6 +275,11 @@ class MediaCarouselController @Inject constructor( updatePageIndicator() mediaCarouselScrollHandler.onPlayersChanged() mediaCarousel.requiresRemeasuring = true + // Check postcondition: mediaContent should have the same number of children as there are + // elements in mediaPlayers. + if (mediaPlayers.size != mediaContent.childCount) { + Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync") + } } private fun removePlayer(key: String) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 8cb93bfc6d4d..299ae5b50aa9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -517,22 +517,36 @@ class MediaDataManager( fun onNotificationRemoved(key: String) { Assert.isMainThread() - if (useMediaResumption && mediaEntries.get(key)?.resumeAction != null) { + val removed = mediaEntries.remove(key) + if (useMediaResumption && removed?.resumeAction != null) { Log.d(TAG, "Not removing $key because resumable") - // Move to resume key aka package name - val data = mediaEntries.remove(key)!! - val resumeAction = getResumeMediaAction(data.resumeAction!!) - val updated = data.copy(token = null, actions = listOf(resumeAction), + // Move to resume key (aka package name) if that key doesn't already exist. + val resumeAction = getResumeMediaAction(removed.resumeAction!!) + val updated = removed.copy(token = null, actions = listOf(resumeAction), actionsToShowInCompact = listOf(0), active = false, resumption = true) - mediaEntries.put(data.packageName, updated) - // Notify listeners of "new" controls + val pkg = removed?.packageName + val migrate = mediaEntries.put(pkg, updated) == null + // Notify listeners of "new" controls when migrating or removed and update when not val listenersCopy = listeners.toSet() - listenersCopy.forEach { - it.onMediaDataLoaded(data.packageName, key, updated) + if (migrate) { + listenersCopy.forEach { + it.onMediaDataLoaded(pkg, key, updated) + } + } else { + // Since packageName is used for the key of the resumption controls, it is + // possible that another notification has already been reused for the resumption + // controls of this package. In this case, rather than renaming this player as + // packageName, just remove it and then send a update to the existing resumption + // controls. + listenersCopy.forEach { + it.onMediaDataRemoved(key) + } + listenersCopy.forEach { + it.onMediaDataLoaded(pkg, pkg, updated) + } } return } - val removed = mediaEntries.remove(key) if (removed != null) { val listenersCopy = listeners.toSet() listenersCopy.forEach { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java index c454048d0649..daf8ca324c74 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java @@ -104,4 +104,13 @@ public class QSDetailClipper { public void showBackground() { mBackground.showSecondLayer(); } + + /** + * Cancels the animator if it's running. + */ + public void cancelAnimator() { + if (mAnimator != null) { + mAnimator.cancel(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 3e2f9dec5807..e5ed88c10a2e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -208,6 +208,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene public void showImmediately() { if (!isShown) { setVisibility(VISIBLE); + mClipper.cancelAnimator(); mClipper.showBackground(); isShown = true; setTileSpecs(); @@ -230,6 +231,10 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED); isShown = false; mToolbar.dismissPopupMenus(); + mClipper.cancelAnimator(); + // Make sure we're not opening (because we're closing). Nobody can think we are + // customizing after the next two lines. + mOpening = false; setCustomizing(false); save(); if (animate) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java index fba7a22351e2..201ed9c9ebec 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -21,7 +21,6 @@ import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_ import android.content.Context; import android.content.Intent; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; @@ -33,6 +32,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.systemui.R; import com.android.systemui.qs.PseudoGridView; import com.android.systemui.qs.QSUserSwitcherEvent; @@ -100,7 +100,9 @@ public class UserDetailView extends PseudoGridView { if (item.picture == null) { v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId()); } else { - Drawable drawable = new BitmapDrawable(v.getResources(), item.picture); + int avatarSize = + (int) mContext.getResources().getDimension(R.dimen.qs_framed_avatar_size); + Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize); drawable.setColorFilter( item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter()); v.bind(name, drawable, item.info.id); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 732f25f90eb5..e942d85790c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -371,12 +371,26 @@ public abstract class PanelViewController { float vectorVel = (float) Math.hypot( mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); - boolean expand = flingExpands(vel, vectorVel, x, y) - || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel; + final boolean onKeyguard = + mStatusBarStateController.getState() == StatusBarState.KEYGUARD; + + final boolean expand; + if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) { + // If we get a cancel, put the shade back to the state it was in when the gesture + // started + if (onKeyguard) { + expand = true; + } else { + expand = !mPanelClosedOnDown; + } + } else { + expand = flingExpands(vel, vectorVel, x, y); + } + mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold, mStatusBar.isFalsingThresholdNeeded(), mStatusBar.isWakeUpComingFromTouch()); // Log collapse gesture if on lock screen. - if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { + if (!expand && onKeyguard) { float displayDensity = mStatusBar.getDisplayDensity(); int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity); int velocityDp = (int) Math.abs(vel / displayDensity); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java index 512d0f3910f8..f52a6e0191a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java @@ -24,7 +24,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.database.DataSetObserver; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; @@ -36,6 +35,7 @@ import android.view.ViewStub; import android.widget.FrameLayout; import com.android.settingslib.animation.AppearAnimationUtils; +import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -288,7 +288,9 @@ public class KeyguardUserSwitcher { if (item.picture == null) { v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId()); } else { - Drawable drawable = new BitmapDrawable(v.getResources(), item.picture); + int avatarSize = + (int) mContext.getResources().getDimension(R.dimen.kg_framed_avatar_size); + Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize); drawable.setColorFilter( item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter()); v.bind(name, drawable, item.info.id); diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index 8ba5b9951c54..c0089e53f8b6 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -226,6 +226,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (!activeControl.getSurfacePosition().equals(lastSurfacePosition) && mAnimation != null) { startAnimation(mImeShowing, true /* forceRestart */); + } else if (!mImeShowing) { + removeImeSurface(); } }); } @@ -370,16 +372,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged dispatchEndPositioning(mDisplayId, mCancelled, t); if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) { t.hide(mImeSourceControl.getLeash()); - final IInputMethodManager imms = getImms(); - if (imms != null) { - try { - // Remove the IME surface to make the insets invisible for - // non-client controlled insets. - imms.removeImeSurface(); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to remove IME surface.", e); - } - } + removeImeSurface(); } t.apply(); mTransactionPool.release(t); @@ -402,6 +395,19 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } + void removeImeSurface() { + final IInputMethodManager imms = getImms(); + if (imms != null) { + try { + // Remove the IME surface to make the insets invisible for + // non-client controlled insets. + imms.removeImeSurface(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to remove IME surface.", e); + } + } + } + /** * Allows other things to synchronize with the ime position */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 6761b282b26a..59c2d0e86c56 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -31,6 +31,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever private const val KEY = "KEY" +private const val KEY_2 = "KEY_2" private const val PACKAGE_NAME = "com.android.systemui" private const val APP_NAME = "SystemUI" private const val SESSION_ARTIST = "artist" @@ -156,8 +157,43 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) // WHEN the notification is removed mediaDataManager.onNotificationRemoved(KEY) - // THEN the media data indicates that it is + // THEN the media data indicates that it is for resumption + assertThat(listener.data!!.resumption).isTrue() + // AND the new key is the package name + assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) + assertThat(listener.oldKey!!).isEqualTo(KEY) + assertThat(listener.removedKey).isNull() + } + + @Test + fun testOnNotificationRemoved_twoWithResumption() { + // GIVEN that the manager has two notifications with resume actions + val listener = TestListener() + mediaDataManager.addListener(listener) + whenever(controller.metadata).thenReturn(metadataBuilder.build()) + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + mediaDataManager.onNotificationAdded(KEY_2, mediaNotification) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(2) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(2) + val data = listener.data!! + assertThat(data.resumption).isFalse() + val resumableData = data.copy(resumeAction = Runnable {}) + mediaDataManager.onMediaDataLoaded(KEY, null, resumableData) + mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData) + // WHEN the first is removed + mediaDataManager.onNotificationRemoved(KEY) + // THEN the data is for resumption and the key is migrated to the package name + assertThat(listener.data!!.resumption).isTrue() + assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) + assertThat(listener.oldKey!!).isEqualTo(KEY) + assertThat(listener.removedKey).isNull() + // WHEN the second is removed + mediaDataManager.onNotificationRemoved(KEY_2) + // THEN the data is for resumption and the second key is removed assertThat(listener.data!!.resumption).isTrue() + assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) + assertThat(listener.oldKey!!).isEqualTo(PACKAGE_NAME) + assertThat(listener.removedKey!!).isEqualTo(KEY_2) } @Test @@ -190,6 +226,7 @@ class MediaDataManagerTest : SysuiTestCase() { var data: MediaData? = null var key: String? = null var oldKey: String? = null + var removedKey: String? = null override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { this.key = key @@ -198,9 +235,7 @@ class MediaDataManagerTest : SysuiTestCase() { } override fun onMediaDataRemoved(key: String) { - this.key = key - oldKey = null - data = null + removedKey = key } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt index 6d6a4d8f6b7d..f48b3fc51e82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt @@ -25,6 +25,8 @@ import android.view.View import android.view.ViewGroup import androidx.test.filters.SmallTest import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.internal.util.UserIcons +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.qs.QSUserSwitcherEvent import com.android.systemui.statusbar.policy.UserSwitcherController @@ -50,10 +52,10 @@ class UserDetailViewAdapterTest : SysuiTestCase() { @Mock private lateinit var mOtherView: View @Mock private lateinit var mInflatedUserDetailItemView: UserDetailItemView @Mock private lateinit var mUserInfo: UserInfo - @Mock private lateinit var mPicture: Bitmap @Mock private lateinit var mLayoutInflater: LayoutInflater private lateinit var adapter: UserDetailView.Adapter private lateinit var uiEventLogger: UiEventLoggerFake + private lateinit var mPicture: Bitmap @Before fun setUp() { @@ -64,6 +66,7 @@ class UserDetailViewAdapterTest : SysuiTestCase() { `when`(mLayoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean())) .thenReturn(mInflatedUserDetailItemView) adapter = UserDetailView.Adapter(mContext, mUserSwitcherController, uiEventLogger) + mPicture = UserIcons.convertToBitmap(mContext.getDrawable(R.drawable.ic_avatar_user)) } private fun clickableTest( @@ -141,4 +144,4 @@ class UserDetailViewAdapterTest : SysuiTestCase() { false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */) -}
\ No newline at end of file +} diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 74df11370e50..e10bab4b36b5 100644 --- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -42,6 +42,7 @@ import android.net.dhcp.DhcpPacket; import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; +import android.os.SystemProperties; import android.system.Os; import android.util.Log; @@ -224,9 +225,19 @@ public class EthernetTetheringTest { } + private boolean isAdbOverNetwork() { + // If adb TCP port opened, this test may running by adb over network. + return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) + || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); + } + @Test public void testPhysicalEthernet() throws Exception { assumeTrue(mEm.isAvailable()); + // Do not run this test if adb is over network and ethernet is connected. + // It is likely the adb run over ethernet, the adb would break when ethernet is switching + // from client mode to server mode. See b/160389275. + assumeFalse(isAdbOverNetwork()); // Get an interface to use. final String iface = mTetheredInterfaceRequester.getInterface(); diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java index c8cee1079e8e..070626be9f80 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java @@ -322,6 +322,12 @@ class EventDispatcher { return true; } + void clear() { + mLongPressingPointerId = -1; + mLongPressingPointerDeltaX = 0; + mLongPressingPointerDeltaY = 0; + } + public void clickWithTouchEvents(MotionEvent event, MotionEvent rawEvent, int policyFlags) { final int pointerIndex = event.getActionIndex(); final int pointerId = event.getPointerId(pointerIndex); diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 6f2e6263b937..fbc986bdd730 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -809,6 +809,7 @@ public class TouchExplorer extends BaseEventStreamTransformation // Announce the end of a the touch interaction. mAms.onTouchInteractionEnd(); + mDispatcher.clear(); mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); } break; diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java index 68eeb0a3ca2e..b2daae48bb0e 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java @@ -103,6 +103,8 @@ final class AutofillInlineSuggestionsRequestSession { private boolean mDestroyed = false; @GuardedBy("mLock") private boolean mPreviousHasNonPinSuggestionShow; + @GuardedBy("mLock") + private boolean mImeSessionInvalidated = false; AutofillInlineSuggestionsRequestSession( @NonNull InputMethodManagerInternal inputMethodManagerInternal, int userId, @@ -157,7 +159,7 @@ final class AutofillInlineSuggestionsRequestSession { Slog.d(TAG, "onInlineSuggestionsResponseLocked called for:" + inlineFillUi.getAutofillId()); } - if (mImeRequest == null || mResponseCallback == null) { + if (mImeRequest == null || mResponseCallback == null || mImeSessionInvalidated) { return false; } // TODO(b/151123764): each session should only correspond to one field. @@ -191,6 +193,7 @@ final class AutofillInlineSuggestionsRequestSession { if (mDestroyed) { return; } + mImeSessionInvalidated = false; if (sDebug) Slog.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId); mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId, new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras), @@ -291,6 +294,7 @@ final class AutofillInlineSuggestionsRequestSession { return; } mImeRequestReceived = true; + mImeSessionInvalidated = false; if (request != null && callback != null) { mImeRequest = request; @@ -346,6 +350,20 @@ final class AutofillInlineSuggestionsRequestSession { } } + /** + * Handles the IME session status received from the IME. + * + * <p> Should only be invoked in the {@link #mHandler} thread. + */ + private void handleOnReceiveImeSessionInvalidated() { + synchronized (mLock) { + if (mDestroyed) { + return; + } + mImeSessionInvalidated = true; + } + } + private static final class InlineSuggestionsRequestCallbackImpl extends IInlineSuggestionsRequestCallback.Stub { @@ -433,6 +451,18 @@ final class AutofillInlineSuggestionsRequestSession { session, false, false)); } } + + @BinderThread + @Override + public void onInlineSuggestionsSessionInvalidated() throws RemoteException { + if (sDebug) Slog.d(TAG, "onInlineSuggestionsSessionInvalidated() called."); + final AutofillInlineSuggestionsRequestSession session = mSession.get(); + if (session != null) { + session.mHandler.sendMessage(obtainMessage( + AutofillInlineSuggestionsRequestSession + ::handleOnReceiveImeSessionInvalidated, session)); + } + } } private static boolean match(@Nullable AutofillId autofillId, diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 5124c4a4797e..a2eea1348d5c 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -470,22 +470,23 @@ public final class BroadcastQueue { // if this receiver was slow, impose deferral policy on the app. This will kick in // when processNextBroadcastLocked() next finds this uid as a receiver identity. if (!r.timeoutExempt) { - if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) { + // r.curApp can be null if finish has raced with process death - benign + // edge case, and we just ignore it because we're already cleaning up + // as expected. + if (r.curApp != null + && mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) { // Core system packages are exempt from deferral policy if (!UserHandle.isCore(r.curApp.uid)) { if (DEBUG_BROADCAST_DEFERRAL) { Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1) + " was slow: " + receiver + " br=" + r); } - if (r.curApp != null) { - mDispatcher.startDeferring(r.curApp.uid); - } else { - Slog.d(TAG_BROADCAST, "finish receiver curApp is null? " + r); - } + mDispatcher.startDeferring(r.curApp.uid); } else { if (DEBUG_BROADCAST_DEFERRAL) { Slog.i(TAG_BROADCAST, "Core uid " + r.curApp.uid - + " receiver was slow but not deferring: " + receiver + " br=" + r); + + " receiver was slow but not deferring: " + + receiver + " br=" + r); } } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index 70f0399d1070..05cf40a091b6 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -120,6 +120,11 @@ public abstract class InputMethodManagerInternal { public abstract void reportImeControl(@Nullable IBinder windowToken); /** + * Destroys the IME surface. + */ + public abstract void removeImeSurface(); + + /** * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing. */ private static final InputMethodManagerInternal NOP = @@ -166,6 +171,10 @@ public abstract class InputMethodManagerInternal { @Override public void reportImeControl(@Nullable IBinder windowToken) { } + + @Override + public void removeImeSurface() { + } }; /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 15e8a92ba0e4..d8ee32e7bd74 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -211,6 +211,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_INITIALIZE_IME = 1040; static final int MSG_CREATE_SESSION = 1050; static final int MSG_REMOVE_IME_SURFACE = 1060; + static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061; static final int MSG_START_INPUT = 2000; @@ -2128,6 +2129,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void onInputMethodFinishInput() throws RemoteException { mCallback.onInputMethodFinishInput(); } + + @Override + public void onInlineSuggestionsSessionInvalidated() throws RemoteException { + mCallback.onInlineSuggestionsSessionInvalidated(); + } } /** @@ -4000,6 +4006,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE)); } + @Override + public void removeImeSurfaceFromWindow(IBinder windowToken) { + // No permission check, because we'll only execute the request if the calling window is + // also the current IME client. + mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget(); + } + @BinderThread private void notifyUserAction(@NonNull IBinder token) { if (DEBUG) { @@ -4273,11 +4286,27 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return true; } case MSG_REMOVE_IME_SURFACE: { - try { - if (mEnabledSession != null && mEnabledSession.session != null) { - mEnabledSession.session.removeImeSurface(); + synchronized (mMethodMap) { + try { + if (mEnabledSession != null && mEnabledSession.session != null + && !mShowRequested) { + mEnabledSession.session.removeImeSurface(); + } + } catch (RemoteException e) { + } + } + return true; + } + case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: { + IBinder windowToken = (IBinder) msg.obj; + synchronized (mMethodMap) { + try { + if (windowToken == mCurFocusedWindow + && mEnabledSession != null && mEnabledSession.session != null) { + mEnabledSession.session.removeImeSurface(); + } + } catch (RemoteException e) { } - } catch (RemoteException e) { } return true; } @@ -5111,6 +5140,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void reportImeControl(@Nullable IBinder windowToken) { mService.reportImeControl(windowToken); } + + @Override + public void removeImeSurface() { + mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE)); + } } @BinderThread diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 2e3d3963c9df..19dff9807075 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -225,6 +225,11 @@ public final class MultiClientInputMethodManagerService { @Override public void reportImeControl(@Nullable IBinder windowToken) { } + + @Override + public void removeImeSurface() { + reportNotSupported(); + } }); } @@ -1473,6 +1478,12 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override + public void removeImeSurfaceFromWindow(IBinder windowToken) { + reportNotSupported(); + } + + @BinderThread + @Override public boolean showSoftInput( IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver) { diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 3a203d52edbe..c3c2e5e65103 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -708,12 +708,15 @@ public class AppsFilter { return ret; } + /** + * This method recomputes all component / intent-based visibility and is intended to match the + * relevant logic of {@link #addPackageInternal(PackageSetting, ArrayMap)} + */ private void recomputeComponentVisibility(ArrayMap<String, PackageSetting> existingSettings) { mQueriesViaComponent.clear(); for (int i = existingSettings.size() - 1; i >= 0; i--) { PackageSetting setting = existingSettings.valueAt(i); - if (setting.pkg == null - || mForceQueryable.contains(setting.appId)) { + if (setting.pkg == null || requestsQueryAllPackages(setting.pkg)) { continue; } for (int j = existingSettings.size() - 1; j >= 0; j--) { @@ -721,7 +724,7 @@ public class AppsFilter { continue; } final PackageSetting otherSetting = existingSettings.valueAt(j); - if (otherSetting.pkg == null) { + if (otherSetting.pkg == null || mForceQueryable.contains(otherSetting.appId)) { continue; } if (canQueryViaComponents(setting.pkg, otherSetting.pkg, mProtectedBroadcasts)) { diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 2df4a920d5c2..eddab76de5ee 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.annotation.Nullable; import android.content.Context; @@ -42,10 +43,9 @@ import java.io.FileDescriptor; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; /** * A service for A/B OTA dexopting. @@ -123,15 +123,20 @@ public class OtaDexoptService extends IOtaDexopt.Stub { } final List<PackageSetting> important; final List<PackageSetting> others; + Predicate<PackageSetting> isPlatformPackage = pkgSetting -> + PLATFORM_PACKAGE_NAME.equals(pkgSetting.pkg.getPackageName()); synchronized (mPackageManagerService.mLock) { // Important: the packages we need to run with ab-ota compiler-reason. important = PackageManagerServiceUtils.getPackagesForDexopt( mPackageManagerService.mSettings.mPackages.values(), mPackageManagerService, DEBUG_DEXOPT); + // Remove Platform Package from A/B OTA b/160735835. + important.removeIf(isPlatformPackage); // Others: we should optimize this with the (first-)boot compiler-reason. others = new ArrayList<>(mPackageManagerService.mSettings.mPackages.values()); others.removeAll(important); others.removeIf(PackageManagerServiceUtils.REMOVE_IF_NULL_PKG); + others.removeIf(isPlatformPackage); // Pre-size the array list by over-allocating by a factor of 1.5. mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2); @@ -147,7 +152,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { throw new IllegalStateException("Found a core app that's not important"); } mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.pkg, pkgSetting, - PackageManagerService.REASON_FIRST_BOOT)); + PackageManagerService.REASON_FIRST_BOOT)); } completeSize = mDexoptCommands.size(); diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 91b2ea1853fa..08cdd8f05292 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -589,12 +589,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } - if (mBypassNextStagedInstallerCheck) { - mBypassNextStagedInstallerCheck = false; - } else if (params.isStaged - && !isCalledBySystemOrShell(callingUid) - && !isWhitelistedStagedInstaller(requestedInstallerPackageName)) { - throw new SecurityException("Installer not allowed to commit staged install"); + if (params.isStaged && !isCalledBySystemOrShell(callingUid)) { + if (mBypassNextStagedInstallerCheck) { + mBypassNextStagedInstallerCheck = false; + } else if (!isStagedInstallerAllowed(requestedInstallerPackageName)) { + throw new SecurityException("Installer not allowed to commit staged install"); + } } if (!params.isMultiPackage) { @@ -725,7 +725,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements || callingUid == Process.SHELL_UID; } - private boolean isWhitelistedStagedInstaller(String installerName) { + private boolean isStagedInstallerAllowed(String installerName) { return SystemConfig.getInstance().getWhitelistedStagedInstallers().contains(installerName); } diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index c38d649ada9b..4b3ddd856c61 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -114,7 +114,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { private static final boolean DEBUG = false; private static final String TAG = "UriGrantsManagerService"; // Maximum number of persisted Uri grants a package is allowed - private static final int MAX_PERSISTED_URI_GRANTS = 128; + private static final int MAX_PERSISTED_URI_GRANTS = 512; private static final boolean ENABLE_DYNAMIC_PERMISSIONS = false; private final Object mLock = new Object(); diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index db4c3ea4cf06..e9768a26f571 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -2443,7 +2443,7 @@ class ActivityStack extends Task { forAllActivities(ActivityRecord::removeLaunchTickRunnable); } - private void updateTransitLocked(int transit, ActivityOptions options) { + private void updateTransitLocked(int transit, ActivityOptions options, boolean forceOverride) { if (options != null) { ActivityRecord r = topRunningActivity(); if (r != null && !r.isState(RESUMED)) { @@ -2452,7 +2452,8 @@ class ActivityStack extends Task { ActivityOptions.abort(options); } } - getDisplay().mDisplayContent.prepareAppTransition(transit, false); + getDisplay().mDisplayContent.prepareAppTransition(transit, false, + 0 /* flags */, forceOverride); } final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options, @@ -2472,8 +2473,17 @@ class ActivityStack extends Task { // nothing to do! if (noAnimation) { ActivityOptions.abort(options); + } else if (isSingleTaskInstance()) { + // When a task is moved front on the display which can only contain one task, start + // a special transition. + // {@link AppTransitionController#handleAppTransitionReady} later picks up the + // transition, and schedules + // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is triggered + // after contents are drawn on the display. + updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options, + true /* forceOverride */); } else { - updateTransitLocked(TRANSIT_TASK_TO_FRONT, options); + updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */); } return; } @@ -2519,9 +2529,13 @@ class ActivityStack extends Task { mStackSupervisor.mNoAnimActivities.add(r); } ActivityOptions.abort(options); + } else if (isSingleTaskInstance()) { + updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options, + true /* forceOverride */); } else { - updateTransitLocked(TRANSIT_TASK_TO_FRONT, options); + updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */); } + // If a new task is moved to the front, then mark the existing top activity as // supporting diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e8a4234f44a7..b94fb0471af4 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3574,12 +3574,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - private void updateImeControlTarget() { + void updateImeControlTarget() { mInputMethodControlTarget = computeImeControlTarget(); mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget); - final WindowState win = mInputMethodControlTarget != null - ? mInputMethodControlTarget.getWindow() : null; + final WindowState win = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget); final IBinder token = win != null ? win.mClient.asBinder() : null; // Note: not allowed to call into IMMS with the WM lock held, hence the post. mWmService.mH.post(() -> @@ -3603,6 +3602,17 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null) { return mRemoteInsetsControlTarget; } else { + // Now, a special case -- if the last target's window is in the process of exiting, but + // not removed, keep on the last target to avoid IME flicker. + final WindowState cur = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget); + if (cur != null && !cur.mRemoved && cur.isDisplayedLw() && cur.isClosing() + && !cur.isActivityTypeHome()) { + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG_WM, "Not changing control while current window" + + " is closing and not removed"); + } + return cur; + } // Otherwise, we just use the ime target as received from IME. return mInputMethodInputTarget; } diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 8298763c1392..99ee5e121b7a 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -18,13 +18,14 @@ package com.android.server.wm; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME; -import android.graphics.PixelFormat; import android.view.InsetsSource; import android.view.WindowInsets; import com.android.internal.annotations.VisibleForTesting; import com.android.server.protolog.common.ProtoLog; +import java.io.PrintWriter; + /** * Controller for IME inset source on the server. It's called provider as it provides the * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}. @@ -132,8 +133,17 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { || (mImeTargetFromIme != null && dcTarget.getParentWindow() == mImeTargetFromIme && dcTarget.mSubLayer > mImeTargetFromIme.mSubLayer) || mImeTargetFromIme == mDisplayContent.getImeFallback() - // If IME target is transparent but control target matches requesting window. - || (controlTarget == mImeTargetFromIme - && PixelFormat.formatHasAlpha(dcTarget.mAttrs.format)); + || (!mImeTargetFromIme.isClosing() && controlTarget == mImeTargetFromIme); + } + + @Override + public void dump(PrintWriter pw, String prefix) { + super.dump(pw, prefix); + if (mImeTargetFromIme != null) { + pw.print(prefix); + pw.print("showImePostLayout pending for mImeTargetFromIme="); + pw.print(mImeTargetFromIme); + pw.println(); + } } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 8734b5efa45d..0216db471843 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -16,13 +16,14 @@ package com.android.server.wm; -import static android.os.Process.myPid; -import static android.os.Process.myUid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS; @@ -477,12 +478,18 @@ final class InputMonitor { mService.getRecentsAnimationController(); final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord); + final int type = w.mAttrs.type; + final boolean isVisible = w.isVisibleLw(); if (inputChannel == null || inputWindowHandle == null || w.mRemoved || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { if (w.mWinAnimator.hasSurface()) { + // Assign an InputInfo with type to the overlay window which can't receive input + // event. This is used to omit Surfaces from occlusion detection. + populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible); mInputTransaction.setInputWindowInfo( - w.mWinAnimator.mSurfaceController.getClientViewRootSurface(), - mInvalidInputWindow); + w.mWinAnimator.mSurfaceController.getClientViewRootSurface(), + mInvalidInputWindow); + return; } // Skip this window because it cannot possibly receive input. return; @@ -490,9 +497,7 @@ final class InputMonitor { final int flags = w.mAttrs.flags; final int privateFlags = w.mAttrs.privateFlags; - final int type = w.mAttrs.type; final boolean hasFocus = w.isFocused(); - final boolean isVisible = w.isVisibleLw(); if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { if (recentsAnimationController.updateInputConsumerForApp( @@ -555,6 +560,26 @@ final class InputMonitor { } } + // This would reset InputWindowHandle fields to prevent it could be found by input event. + // We need to check if any new field of InputWindowHandle could impact the result. + private static void populateOverlayInputInfo(final InputWindowHandle inputWindowHandle, + final String name, final int type, final boolean isVisible) { + inputWindowHandle.name = name; + inputWindowHandle.layoutParamsType = type; + inputWindowHandle.dispatchingTimeoutNanos = + WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + inputWindowHandle.visible = isVisible; + inputWindowHandle.canReceiveKeys = false; + inputWindowHandle.hasFocus = false; + inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL; + inputWindowHandle.scaleFactor = 1; + inputWindowHandle.layoutParamsFlags = + FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE; + inputWindowHandle.portalToDisplayId = INVALID_DISPLAY; + inputWindowHandle.touchableRegion.setEmpty(); + inputWindowHandle.setTouchableRegionCrop(null); + } + /** * Helper function to generate an InputInfo with type SECURE_SYSTEM_OVERLAY. This input * info will not have an input channel or be touchable, but is used to omit Surfaces @@ -564,16 +589,7 @@ final class InputMonitor { static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t, int displayId, String name) { InputWindowHandle inputWindowHandle = new InputWindowHandle(null, displayId); - inputWindowHandle.name = name; - inputWindowHandle.layoutParamsType = TYPE_SECURE_SYSTEM_OVERLAY; - inputWindowHandle.dispatchingTimeoutNanos = -1; - inputWindowHandle.visible = true; - inputWindowHandle.canReceiveKeys = false; - inputWindowHandle.hasFocus = false; - inputWindowHandle.ownerPid = myPid(); - inputWindowHandle.ownerUid = myUid(); - inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL; - inputWindowHandle.scaleFactor = 1; + populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true); t.setInputWindowInfo(sc, inputWindowHandle); } } diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index c50f296504fc..3ffc26a7a8ad 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -62,4 +62,8 @@ interface InsetsControlTarget { return false; } + /** Returns {@code target.getWindow()}, or null if {@code target} is {@code null}. */ + static WindowState asWindowOrNull(InsetsControlTarget target) { + return target != null ? target.getWindow() : null; + } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 63083faaddb1..77bd4a47a884 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -44,6 +44,7 @@ import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; import android.view.WindowManager; +import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.protolog.common.ProtoLog; import java.io.PrintWriter; @@ -74,7 +75,21 @@ class InsetsStateController { w.notifyInsetsChanged(); } }; - private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { }; + private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { + @Override + public void notifyInsetsControlChanged() { + InsetsSourceControl[] controls = getControlsForDispatch(this); + if (controls == null) { + return; + } + for (InsetsSourceControl control : controls) { + if (control.getType() == ITYPE_IME) { + mDisplayContent.mWmService.mH.post(() -> + InputMethodManagerInternal.get().removeImeSurface()); + } + } + } + }; InsetsStateController(DisplayContent displayContent) { mDisplayContent = displayContent; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index c7ffc067427d..4700864c03bc 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2375,7 +2375,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // triggered after contents are drawn on the display. if (display.isSingleTaskInstance()) { display.mDisplayContent.prepareAppTransition( - TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false); + TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false, + 0 /* flags */, true /* forceOverride*/); } stack.awakeFromSleepingLocked(); if (display.isSingleTaskInstance()) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 837fafec1634..00be75fddf04 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2170,6 +2170,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isInputMethodTarget()) { dc.computeImeTarget(true /* updateImeTarget */); } + if (dc.mInputMethodControlTarget == this) { + dc.updateImeControlTarget(); + } final int type = mAttrs.type; if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 77e3c597311c..d64fdb81107c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -898,6 +898,26 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testComputeImeControlTarget_exitingApp() throws Exception { + final DisplayContent dc = createNewDisplay(); + + WindowState exitingWin = createWindow(null, TYPE_BASE_APPLICATION, "exiting app"); + makeWindowVisible(exitingWin); + exitingWin.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; + exitingWin.mAnimatingExit = true; + + dc.mInputMethodControlTarget = exitingWin; + dc.mInputMethodTarget = dc.mInputMethodInputTarget = + createWindow(null, TYPE_BASE_APPLICATION, "starting app"); + + assertEquals(exitingWin, dc.computeImeControlTarget()); + + exitingWin.removeImmediately(); + + assertEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget()); + } + + @Test public void testComputeImeControlTarget_splitscreen() throws Exception { final DisplayContent dc = createNewDisplay(); dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app"); diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 285cf43cd3a1..dee5a98e33e9 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -277,6 +277,11 @@ interface ITelecomService { boolean setDefaultDialer(in String packageName); /** + * Stop suppressing blocked numbers after a call to emergency services. Shell only. + */ + void stopBlockSuppression(); + + /** * @see TelecomServiceImpl#createManageBlockedNumbersIntent **/ Intent createManageBlockedNumbersIntent(); |