diff options
108 files changed, 1430 insertions, 305 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index f36084386f48..0cadbfd23dba 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -456,7 +456,7 @@ public class AppStandbyController implements AppStandbyInternal { mSystemServicesReady = true; // Offload to handler thread to avoid boot time impact. - mHandler.post(mInjector::updatePowerWhitelistCache); + mHandler.post(AppStandbyController.this::updatePowerWhitelistCache); boolean userFileExists; synchronized (mAppIdleLock) { @@ -1684,6 +1684,14 @@ public class AppStandbyController implements AppStandbyInternal { } } + private void updatePowerWhitelistCache() { + if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) { + return; + } + mInjector.updatePowerWhitelistCache(); + postCheckIdleStates(UserHandle.USER_ALL); + } + private class PackageReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -1999,10 +2007,7 @@ public class AppStandbyController implements AppStandbyInternal { } } - private void updatePowerWhitelistCache() { - if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) { - return; - } + void updatePowerWhitelistCache() { try { // Don't call out to DeviceIdleController with the lock held. final String[] whitelistedPkgs = @@ -2204,7 +2209,7 @@ public class AppStandbyController implements AppStandbyInternal { break; case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED: if (mSystemServicesReady) { - mHandler.post(mInjector::updatePowerWhitelistCache); + mHandler.post(AppStandbyController.this::updatePowerWhitelistCache); } break; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 7a016522d597..36b46c3d03a3 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -5178,6 +5178,12 @@ message DataUsageBytesTransfer { // record is combined across opportunistic data subscriptions. // See {@link SubscriptionManager#setOpportunistic}. optional DataSubscriptionState opportunistic_data_sub = 10; + + // Indicate whether NR is connected, server side could use this with RAT type to determine if + // the record is for 5G NSA (Non Stand Alone) mode, where the primary cell is still LTE and + // network allocates a secondary 5G cell so telephony reports RAT = LTE along with NR state as + // connected. + optional bool is_nr_connected = 11; } /** diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index eba7ff348328..7234eb1d81cd 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -87,6 +87,15 @@ public class NetworkTemplate implements Parcelable { * @hide */ public static final int NETWORK_TYPE_ALL = -1; + /** + * Virtual RAT type to represent 5G NSA (Non Stand Alone) mode, where the primary cell is + * still LTE and network allocates a secondary 5G cell so telephony reports RAT = LTE along + * with NR state as connected. This should not be overlapped with any of the + * {@code TelephonyManager.NETWORK_TYPE_*} constants. + * + * @hide + */ + public static final int NETWORK_TYPE_5G_NSA = -2; private static boolean isKnownMatchRule(final int rule) { switch (rule) { @@ -475,6 +484,9 @@ public class NetworkTemplate implements Parcelable { return TelephonyManager.NETWORK_TYPE_LTE; case TelephonyManager.NETWORK_TYPE_NR: return TelephonyManager.NETWORK_TYPE_NR; + // Virtual RAT type for 5G NSA mode, see {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}. + case NetworkTemplate.NETWORK_TYPE_5G_NSA: + return NetworkTemplate.NETWORK_TYPE_5G_NSA; default: return TelephonyManager.NETWORK_TYPE_UNKNOWN; } diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index a0e92b398c4e..276f16216b4d 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -99,6 +99,13 @@ public class CallLog { public static final String LIMIT_PARAM_KEY = "limit"; /** + * Form of {@link #CONTENT_URI} which limits the query results to a single result. + */ + private static final Uri CONTENT_URI_LIMIT_1 = CONTENT_URI.buildUpon() + .appendQueryParameter(LIMIT_PARAM_KEY, "1") + .build(); + + /** * Query parameter used to specify the starting record to return. * <p> * TYPE: integer @@ -932,11 +939,11 @@ public class CallLog { Cursor c = null; try { c = resolver.query( - CONTENT_URI, + CONTENT_URI_LIMIT_1, new String[] {NUMBER}, TYPE + " = " + OUTGOING_TYPE, null, - DEFAULT_SORT_ORDER + " LIMIT 1"); + DEFAULT_SORT_ORDER); if (c == null || !c.moveToFirst()) { return ""; } diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index c861fa774de8..408e8504f408 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -60,6 +60,19 @@ import java.util.Set; * notified of the completion of the initialization.<br> * When you are done using the TextToSpeech instance, call the {@link #shutdown()} method * to release the native resources used by the TextToSpeech engine. + * + * Apps targeting Android 11 that use text-to-speech should declare {@link + * TextToSpeech.Engine#INTENT_ACTION_TTS_SERVICE} in the <code><queries></code> elements of their + * manifest: + * + * <code> + * <queries> + * ... + * <intent> + * <action android:name="android.intent.action.TTS_SERVICE" /> + * </intent> + * </queries> + * </code> */ public class TextToSpeech { @@ -239,6 +252,20 @@ public class TextToSpeech { * through {@link TextToSpeech#getFeatures(java.util.Locale)}. * </li> * </ul> + * + * Apps targeting Android 11 that use text-to-speech should declare {@link + * #INTENT_ACTION_TTS_SERVICE} in the <code><queries></code> elements of their + * manifest: + * + * <code> + * <queries> + * ... + * <intent> + * <action android:name="android.intent.action.TTS_SERVICE" /> + * </intent> + * </queries> + * </code> + */ public class Engine { diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java index ad43f9556d8d..92772c1d7a44 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -125,10 +125,10 @@ public final class ImeFocusController { final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView; onViewFocusChanged(viewForWindowFocus, true); - // Starting new input when the next focused view is same as served view but the - // editor is not aligned with the same editor or editor is inactive. - final boolean nextFocusIsServedView = mServedView != null && mServedView == focusedView; - if (nextFocusIsServedView && !immDelegate.isSameEditorAndAcceptingText(focusedView)) { + // Starting new input when the next focused view is same as served view but the currently + // active connection (if any) is not associated with it. + final boolean nextFocusIsServedView = mServedView == viewForWindowFocus; + if (nextFocusIsServedView && !immDelegate.hasActiveConnection(viewForWindowFocus)) { forceFocus = true; } @@ -254,7 +254,7 @@ public final class ImeFocusController { void setCurrentRootView(ViewRootImpl rootView); boolean isCurrentRootView(ViewRootImpl rootView); boolean isRestartOnNextWindowFocus(boolean reset); - boolean isSameEditorAndAcceptingText(View view); + boolean hasActiveConnection(View view); } public View getServedView() { diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 31da83ad5137..69220722fc05 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -308,7 +308,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll false /* isScreenRound */, false /* alwaysConsumeSystemBars */, null /* displayCutout */, LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/, - 0 /* legacySystemUiFlags */, typeSideMap) + 0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, typeSideMap) .getInsets(mTypes); } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index a679b3740fd9..c383bc7a4d70 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -501,6 +501,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private PendingControlRequest mPendingImeControlRequest; private int mLastLegacySoftInputMode; + private int mLastLegacyWindowFlags; private int mLastLegacySystemUiFlags; private DisplayCutout mLastDisplayCutout; private boolean mStartingAnimation; @@ -569,8 +570,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/, mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(), - mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacySystemUiFlags, - null /* typeSideMap */); + mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacyWindowFlags, + mLastLegacySystemUiFlags, null /* typeSideMap */); mHost.dispatchWindowInsetsAnimationProgress(insets, mUnmodifiableTmpRunningAnims); if (DEBUG) { for (WindowInsetsAnimation anim : mUnmodifiableTmpRunningAnims) { @@ -706,13 +707,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting public WindowInsets calculateInsets(boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout, - int legacySoftInputMode, int legacySystemUiFlags) { + int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags) { mLastLegacySoftInputMode = legacySoftInputMode; + mLastLegacyWindowFlags = legacyWindowFlags; mLastLegacySystemUiFlags = legacySystemUiFlags; mLastDisplayCutout = cutout; mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/, isScreenRound, alwaysConsumeSystemBars, cutout, - legacySoftInputMode, legacySystemUiFlags, + legacySoftInputMode, legacyWindowFlags, legacySystemUiFlags, null /* typeSideMap */); return mLastInsets; } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 91e7591193f1..6b0b509932a8 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -22,13 +22,14 @@ import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME; import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; import static android.view.ViewRootImpl.sNewInsetsMode; import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES; -import static android.view.WindowInsets.Type.SIZE; import static android.view.WindowInsets.Type.SYSTEM_GESTURES; import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; import static android.view.WindowInsets.Type.isVisibleInsetsType; +import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowInsets.Type.systemBars; +import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; @@ -38,7 +39,6 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseIntArray; import android.view.WindowInsets.Type; @@ -171,7 +171,7 @@ public class InsetsState implements Parcelable { */ public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState, boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout, - int legacySoftInputMode, int legacySystemUiFlags, + int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags, @Nullable @InternalInsetsSide SparseIntArray typeSideMap) { Insets[] typeInsetsMap = new Insets[Type.SIZE]; Insets[] typeMaxInsetsMap = new Insets[Type.SIZE]; @@ -218,10 +218,17 @@ public class InsetsState implements Parcelable { } } final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST; + + @InsetsType int compatInsetsTypes = systemBars() | displayCutout(); + if (softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE) { + compatInsetsTypes |= ime(); + } + if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) { + compatInsetsTypes &= ~statusBars(); + } + return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound, - alwaysConsumeSystemBars, cutout, softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE - ? systemBars() | displayCutout() | ime() - : systemBars() | displayCutout(), + alwaysConsumeSystemBars, cutout, compatInsetsTypes, sNewInsetsMode == NEW_INSETS_MODE_FULL && (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index fefe564787ca..7453f21d379b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2265,7 +2265,8 @@ public final class ViewRootImpl implements ViewParent, mLastWindowInsets = mInsetsController.calculateInsets( mContext.getResources().getConfiguration().isScreenRound(), mAttachInfo.mAlwaysConsumeSystemBars, mPendingDisplayCutout.get(), - mWindowAttributes.softInputMode, (mWindowAttributes.systemUiVisibility + mWindowAttributes.softInputMode, mWindowAttributes.flags, + (mWindowAttributes.systemUiVisibility | mWindowAttributes.subtreeSystemUiVisibility)); Rect visibleInsets = mInsetsController.calculateVisibleInsets( diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 4d6b72f96aab..5e94758b003c 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -17,7 +17,6 @@ package android.view; -import static android.view.WindowInsets.Type.CAPTION_BAR; import static android.view.WindowInsets.Type.DISPLAY_CUTOUT; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.IME; @@ -95,7 +94,7 @@ public final class WindowInsets { private final boolean mStableInsetsConsumed; private final boolean mDisplayCutoutConsumed; - private final int mCompatInsetTypes; + private final int mCompatInsetsTypes; private final boolean mCompatIgnoreVisibility; /** @@ -150,8 +149,8 @@ public final class WindowInsets { @Nullable Insets[] typeMaxInsetsMap, boolean[] typeVisibilityMap, boolean isRound, - boolean alwaysConsumeSystemBars, DisplayCutout displayCutout, int compatInsetTypes, - boolean compatIgnoreVisibility) { + boolean alwaysConsumeSystemBars, DisplayCutout displayCutout, + @InsetsType int compatInsetsTypes, boolean compatIgnoreVisibility) { mSystemWindowInsetsConsumed = typeInsetsMap == null; mTypeInsetsMap = mSystemWindowInsetsConsumed ? new Insets[SIZE] @@ -165,7 +164,7 @@ public final class WindowInsets { mTypeVisibilityMap = typeVisibilityMap; mIsRound = isRound; mAlwaysConsumeSystemBars = alwaysConsumeSystemBars; - mCompatInsetTypes = compatInsetTypes; + mCompatInsetsTypes = compatInsetsTypes; mCompatIgnoreVisibility = compatIgnoreVisibility; mDisplayCutoutConsumed = displayCutout == null; @@ -183,7 +182,7 @@ public final class WindowInsets { src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap, src.mTypeVisibilityMap, src.mIsRound, src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src), - src.mCompatInsetTypes, + src.mCompatInsetsTypes, src.mCompatIgnoreVisibility); } @@ -310,11 +309,11 @@ public final class WindowInsets { @NonNull public Insets getSystemWindowInsets() { Insets result = mCompatIgnoreVisibility - ? getInsetsIgnoringVisibility(mCompatInsetTypes & ~ime()) - : getInsets(mCompatInsetTypes); + ? getInsetsIgnoringVisibility(mCompatInsetsTypes & ~ime()) + : getInsets(mCompatInsetsTypes); // We can't query max insets for IME, so we need to add it manually after. - if ((mCompatInsetTypes & ime()) != 0 && mCompatIgnoreVisibility) { + if ((mCompatInsetsTypes & ime()) != 0 && mCompatIgnoreVisibility) { result = Insets.max(result, getInsets(ime())); } return result; @@ -503,7 +502,7 @@ public final class WindowInsets { mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars, null /* displayCutout */, - mCompatInsetTypes, mCompatIgnoreVisibility); + mCompatInsetsTypes, mCompatIgnoreVisibility); } @@ -554,7 +553,7 @@ public final class WindowInsets { mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(this), - mCompatInsetTypes, mCompatIgnoreVisibility); + mCompatInsetsTypes, mCompatIgnoreVisibility); } // TODO(b/119190588): replace @code with @link below @@ -627,7 +626,7 @@ public final class WindowInsets { @Deprecated @NonNull public Insets getStableInsets() { - return getInsets(mTypeMaxInsetsMap, mCompatInsetTypes); + return getInsets(mTypeMaxInsetsMap, mCompatInsetsTypes); } /** @@ -939,7 +938,7 @@ public final class WindowInsets { : mDisplayCutout == null ? DisplayCutout.NO_CUTOUT : mDisplayCutout.inset(left, top, right, bottom), - mCompatInsetTypes, mCompatIgnoreVisibility); + mCompatInsetsTypes, mCompatIgnoreVisibility); } @Override diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 28a18da37b3e..2fe7c021bb21 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -276,7 +276,7 @@ public final class WindowManagerImpl implements WindowManager { if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) { return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/, isScreenRound, alwaysConsumeSystemBars, displayCutout.get(), - SOFT_INPUT_ADJUST_NOTHING, + SOFT_INPUT_ADJUST_NOTHING, attrs.flags, SYSTEM_UI_FLAG_VISIBLE, null /* typeSideMap */); } else { return new WindowInsets.Builder() diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index 492ab6f8a3d5..8c355202b63f 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -49,6 +49,13 @@ public interface WindowManagerPolicyConstants { int PRESENCE_INTERNAL = 1 << 0; int PRESENCE_EXTERNAL = 1 << 1; + // Alternate bars position values + int ALT_BAR_UNKNOWN = -1; + int ALT_BAR_LEFT = 1 << 0; + int ALT_BAR_RIGHT = 1 << 1; + int ALT_BAR_BOTTOM = 1 << 2; + int ALT_BAR_TOP = 1 << 3; + // Navigation bar position values int NAV_BAR_INVALID = -1; int NAV_BAR_LEFT = 1 << 0; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 37b352940ee4..0d21673dfc71 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -19,8 +19,8 @@ package android.view.inputmethod; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; -import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR; -import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR; +import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; +import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION; import android.annotation.DrawableRes; import android.annotation.NonNull; @@ -89,6 +89,7 @@ import com.android.internal.view.InputBindResult; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collections; @@ -610,14 +611,13 @@ public final class InputMethodManager { @Override public void startInputAsyncOnWindowFocusGain(View focusedView, @SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) { - final boolean forceNewFocus1 = forceNewFocus; final int startInputFlags = getStartInputFlags(focusedView, 0); final ImeFocusController controller = getFocusController(); if (controller == null) { return; } - if (controller.checkFocus(forceNewFocus1, false)) { + if (controller.checkFocus(forceNewFocus, false)) { // We need to restart input on the current focus view. This // should be done in conjunction with telling the system service // about the window gaining focus, to help make the transition @@ -633,15 +633,15 @@ public final class InputMethodManager { // we'll just do a window focus gain and call it a day. try { View servedView = controller.getServedView(); - boolean nextFocusSameEditor = servedView != null && servedView == focusedView - && isSameEditorAndAcceptingText(focusedView); + boolean nextFocusHasConnection = servedView != null && servedView == focusedView + && hasActiveConnection(focusedView); if (DEBUG) { Log.v(TAG, "Reporting focus gain, without startInput" - + ", nextFocusIsServedView=" + nextFocusSameEditor); + + ", nextFocusIsServedView=" + nextFocusHasConnection); } final int startInputReason = - nextFocusSameEditor ? WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR - : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR; + nextFocusHasConnection ? WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION + : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; mService.startInputOrWindowGainedFocus( startInputReason, mClient, focusedView.getWindowToken(), startInputFlags, softInputMode, @@ -701,33 +701,24 @@ public final class InputMethodManager { } /** - * For {@link ImeFocusController} to check if the given focused view aligns with the same - * editor and the editor is active to accept the text input. + * Checks whether the active input connection (if any) is for the given view. * - * TODO(b/160968797): Remove this method and move mCurrentTextBoxAttritube to + * TODO(b/160968797): Remove this method and move mServedInputConnectionWrapper to * ImeFocusController. - * In the long-term, we should make mCurrentTextBoxAtrtribue as per-window base instance, - * so that we we can directly check if the current focused view aligned with the same editor - * in the window without using this checking. * - * Note that this method is only use for fixing start new input may ignored issue + * Note that this method is only intended for restarting input after focus gain * (e.g. b/160391516), DO NOT leverage this method to do another check. */ - public boolean isSameEditorAndAcceptingText(View view) { + @Override + public boolean hasActiveConnection(View view) { synchronized (mH) { - if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null) { + if (!hasServedByInputMethodLocked(view)) { return false; } - final EditorInfo ic = mCurrentTextBoxAttribute; - // This sameEditor checking is based on using object hash comparison to check if - // some fields of the current EditorInfo (e.g. autoFillId, OpPackageName) the - // hash code is same as the given focused view. - final boolean sameEditor = view.onCheckIsTextEditor() && view.getId() == ic.fieldId - && view.getAutofillId() == ic.autofillId - && view.getContext().getOpPackageName() == ic.packageName; - return sameEditor && mServedInputConnectionWrapper != null - && mServedInputConnectionWrapper.isActive(); + return mServedInputConnectionWrapper != null + && mServedInputConnectionWrapper.isActive() + && mServedInputConnectionWrapper.mServedView.get() == view; } } } @@ -980,11 +971,13 @@ public final class InputMethodManager { private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper { private final InputMethodManager mParentInputMethodManager; + private final WeakReference<View> mServedView; - public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn, - final InputMethodManager inputMethodManager) { + ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn, + InputMethodManager inputMethodManager, View servedView) { super(mainLooper, conn); mParentInputMethodManager = inputMethodManager; + mServedView = new WeakReference<>(servedView); } @Override @@ -1007,6 +1000,7 @@ public final class InputMethodManager { + "connection=" + getInputConnection() + " finished=" + isFinished() + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive + + " mServedView=" + mServedView.get() + "}"; } } @@ -1187,7 +1181,8 @@ public final class InputMethodManager { mMainLooper = looper; mH = new H(looper); mDisplayId = displayId; - mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this); + mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this, + null); } /** @@ -1968,7 +1963,7 @@ public final class InputMethodManager { icHandler = ic.getHandler(); } servedContext = new ControlledInputConnectionWrapper( - icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this); + icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this, view); } else { servedContext = null; missingMethodFlags = 0; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 226f57976ec8..e16bf5a18808 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -395,6 +395,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private static final int EMS = LINES; private static final int PIXELS = 2; + // Maximum text length for single line input. + private static final int MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT = 5000; + private InputFilter.LengthFilter mSingleLineLengthFilter = null; + private static final RectF TEMP_RECTF = new RectF(); /** @hide */ @@ -1589,7 +1593,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Same as setSingleLine(), but make sure the transformation method and the maximum number // of lines of height are unchanged for multi-line TextViews. setInputTypeSingleLine(singleLine); - applySingleLine(singleLine, singleLine, singleLine); + applySingleLine(singleLine, singleLine, singleLine, + // Does not apply automated max length filter since length filter will be resolved + // later in this function. + false + ); if (singleLine && getKeyListener() == null && ellipsize == ELLIPSIZE_NOT_SET) { ellipsize = ELLIPSIZE_END; @@ -1633,7 +1641,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setTransformationMethod(PasswordTransformationMethod.getInstance()); } - if (maxlength >= 0) { + // For addressing b/145128646 + // For the performance reason, we limit characters for single line text field. + if (bufferType == BufferType.EDITABLE && singleLine && maxlength == -1) { + mSingleLineLengthFilter = new InputFilter.LengthFilter( + MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); + } + + if (mSingleLineLengthFilter != null) { + setFilters(new InputFilter[] { mSingleLineLengthFilter }); + } else if (maxlength >= 0) { setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxlength) }); } else { setFilters(NO_FILTERS); @@ -6590,7 +6607,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mSingleLine != singleLine || forceUpdate) { // Change single line mode, but only change the transformation if // we are not in password mode. - applySingleLine(singleLine, !isPassword, true); + applySingleLine(singleLine, !isPassword, true, true); } if (!isSuggestionsEnabled()) { @@ -10229,6 +10246,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * Note that the default conditions are not necessarily those that were in effect prior this * method, and you may want to reset these properties to your custom values. * + * Note that due to performance reasons, by setting single line for the EditText, the maximum + * text length is set to 5000 if no other character limitation are applied. + * * @attr ref android.R.styleable#TextView_singleLine */ @android.view.RemotableViewMethod @@ -10236,7 +10256,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Could be used, but may break backward compatibility. // if (mSingleLine == singleLine) return; setInputTypeSingleLine(singleLine); - applySingleLine(singleLine, true, true); + applySingleLine(singleLine, true, true, true); } /** @@ -10256,14 +10276,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void applySingleLine(boolean singleLine, boolean applyTransformation, - boolean changeMaxLines) { + boolean changeMaxLines, boolean changeMaxLength) { mSingleLine = singleLine; + if (singleLine) { setLines(1); setHorizontallyScrolling(true); if (applyTransformation) { setTransformationMethod(SingleLineTransformationMethod.getInstance()); } + + if (!changeMaxLength) return; + + // Single line length filter is only applicable editable text. + if (mBufferType != BufferType.EDITABLE) return; + + final InputFilter[] prevFilters = getFilters(); + for (InputFilter filter: getFilters()) { + // We don't add LengthFilter if already there. + if (filter instanceof InputFilter.LengthFilter) return; + } + + if (mSingleLineLengthFilter == null) { + mSingleLineLengthFilter = new InputFilter.LengthFilter( + MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); + } + + final InputFilter[] newFilters = new InputFilter[prevFilters.length + 1]; + System.arraycopy(prevFilters, 0, newFilters, 0, prevFilters.length); + newFilters[prevFilters.length] = mSingleLineLengthFilter; + + setFilters(newFilters); + + // Since filter doesn't apply to existing text, trigger filter by setting text. + setText(getText()); } else { if (changeMaxLines) { setMaxLines(Integer.MAX_VALUE); @@ -10272,6 +10318,47 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (applyTransformation) { setTransformationMethod(null); } + + if (!changeMaxLength) return; + + // Single line length filter is only applicable editable text. + if (mBufferType != BufferType.EDITABLE) return; + + final InputFilter[] prevFilters = getFilters(); + if (prevFilters.length == 0) return; + + // Short Circuit: if mSingleLineLengthFilter is not allocated, nobody sets automated + // single line char limit filter. + if (mSingleLineLengthFilter == null) return; + + // If we need to remove mSingleLineLengthFilter, we need to allocate another array. + // Since filter list is expected to be small and want to avoid unnecessary array + // allocation, check if there is mSingleLengthFilter first. + int targetIndex = -1; + for (int i = 0; i < prevFilters.length; ++i) { + if (prevFilters[i] == mSingleLineLengthFilter) { + targetIndex = i; + break; + } + } + if (targetIndex == -1) return; // not found. Do nothing. + + if (prevFilters.length == 1) { + setFilters(NO_FILTERS); + return; + } + + // Create new array which doesn't include mSingleLengthFilter. + final InputFilter[] newFilters = new InputFilter[prevFilters.length - 1]; + System.arraycopy(prevFilters, 0, newFilters, 0, targetIndex); + System.arraycopy( + prevFilters, + targetIndex + 1, + newFilters, + targetIndex, + prevFilters.length - targetIndex - 1); + setFilters(newFilters); + mSingleLineLengthFilter = null; } } @@ -10900,17 +10987,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * not among the handles. */ boolean isFromPrimePointer(MotionEvent event, boolean fromHandleView) { + boolean res = true; if (mPrimePointerId == NO_POINTER_ID) { mPrimePointerId = event.getPointerId(0); mIsPrimePointerFromHandleView = fromHandleView; } else if (mPrimePointerId != event.getPointerId(0)) { - return mIsPrimePointerFromHandleView && fromHandleView; + res = mIsPrimePointerFromHandleView && fromHandleView; } if (event.getActionMasked() == MotionEvent.ACTION_UP || event.getActionMasked() == MotionEvent.ACTION_CANCEL) { mPrimePointerId = -1; } - return true; + return res; } @Override diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index fbf050ad08f6..3a89dcd96487 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -3134,6 +3134,7 @@ public class ChooserActivity extends ResolverActivity implements // ends up disabled. That's because at some point the old tab's vertical scrolling is // disabled and the new tab's is enabled. For context, see b/159997845 setVerticalScrollEnabled(true); + mResolverDrawerLayout.scrollNestedScrollableChildBackToTop(); } @Override diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index 085cdfcf9462..37f68233db53 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -46,10 +46,10 @@ public final class InputMethodDebug { return "UNSPECIFIED"; case StartInputReason.WINDOW_FOCUS_GAIN: return "WINDOW_FOCUS_GAIN"; - case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR: - return "WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR"; - case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR: - return "WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR"; + case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION: + return "WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION"; + case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION: + return "WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION"; case StartInputReason.APP_CALLED_RESTART_INPUT_API: return "APP_CALLED_RESTART_INPUT_API"; case StartInputReason.CHECK_FOCUS: diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java index 946ce858c12d..2ba708dd9312 100644 --- a/core/java/com/android/internal/inputmethod/StartInputReason.java +++ b/core/java/com/android/internal/inputmethod/StartInputReason.java @@ -30,8 +30,8 @@ import java.lang.annotation.Retention; @IntDef(value = { StartInputReason.UNSPECIFIED, StartInputReason.WINDOW_FOCUS_GAIN, - StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR, - StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR, + StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION, + StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION, StartInputReason.APP_CALLED_RESTART_INPUT_API, StartInputReason.CHECK_FOCUS, StartInputReason.BOUND_TO_IMMS, @@ -54,13 +54,13 @@ public @interface StartInputReason { * view and its input connection remains. {@link android.view.inputmethod.InputMethodManager} * just reports this window focus change event to sync IME input target for system. */ - int WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR = 2; + int WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION = 2; /** * {@link android.view.Window} gained focus but there is no {@link android.view.View} that is * eligible to have IME focus. {@link android.view.inputmethod.InputMethodManager} just reports * this window focus change event for logging. */ - int WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR = 3; + int WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION = 3; /** * {@link android.view.inputmethod.InputMethodManager#restartInput(android.view.View)} is * either explicitly called by the application or indirectly called by some Framework class diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 3f708f84750c..90eeabb47e9a 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -464,11 +464,7 @@ public class ResolverDrawerLayout extends ViewGroup { smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel); mDismissOnScrollerFinished = true; } else { - if (isNestedListChildScrolled()) { - mNestedListChild.smoothScrollToPosition(0); - } else if (isNestedRecyclerChildScrolled()) { - mNestedRecyclerChild.smoothScrollToPosition(0); - } + scrollNestedScrollableChildBackToTop(); smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel); } } @@ -493,6 +489,17 @@ public class ResolverDrawerLayout extends ViewGroup { return handled; } + /** + * Scroll nested scrollable child back to top if it has been scrolled. + */ + public void scrollNestedScrollableChildBackToTop() { + if (isNestedListChildScrolled()) { + mNestedListChild.smoothScrollToPosition(0); + } else if (isNestedRecyclerChildScrolled()) { + mNestedRecyclerChild.smoothScrollToPosition(0); + } + } + private void onSecondaryPointerUp(MotionEvent ev) { final int pointerIndex = ev.getActionIndex(); final int pointerId = ev.getPointerId(pointerIndex); diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 8a4676dec6b3..2f1bcdc76afb 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1259,7 +1259,12 @@ <!-- Can be combined with <var>text</var> and its variations to allow multiple lines of text in the field. If this flag is not set, the text field will be constrained to a single line. Corresponds to - {@link android.text.InputType#TYPE_TEXT_FLAG_MULTI_LINE}. --> + {@link android.text.InputType#TYPE_TEXT_FLAG_MULTI_LINE}. + + Note: If this flag is not set and the text field doesn't have max length limit, the + framework automatically set maximum length of the characters to 5000 for the + performance reasons. + --> <flag name="textMultiLine" value="0x00020001" /> <!-- Can be combined with <var>text</var> and its variations to indicate that though the regular text view should not be multiple diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index bfcf52af80bf..eb695258142a 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -86,7 +86,7 @@ public class ImeInsetsSourceConsumerTest { false, new DisplayCutout( Insets.of(10, 10, 10, 10), rect, rect, rect, rect), - SOFT_INPUT_ADJUST_RESIZE, 0); + SOFT_INPUT_ADJUST_RESIZE, 0, 0); mImeConsumer = (ImeInsetsSourceConsumer) mController.getSourceConsumer(ITYPE_IME); }); } diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index c36f1067149e..801cd4ddb94e 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -165,7 +165,7 @@ public class InsetsControllerTest { false, new DisplayCutout( Insets.of(10, 10, 10, 10), rect, rect, rect, rect), - SOFT_INPUT_ADJUST_RESIZE, 0); + SOFT_INPUT_ADJUST_RESIZE, 0, 0); mController.onFrameChanged(new Rect(0, 0, 100, 100)); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 5260ef83cc4f..c7d835ca7c7e 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -29,6 +29,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; +import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; @@ -77,7 +78,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_IME).setVisible(true); SparseIntArray typeSideMap = new SparseIntArray(); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, typeSideMap); + false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, typeSideMap); assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets()); assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all())); assertEquals(ISIDE_TOP, typeSideMap.get(ITYPE_STATUS_BAR)); @@ -96,7 +97,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_IME).setFrame(new Rect(0, 100, 100, 300)); mState.getSource(ITYPE_IME).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null); + false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, null); assertEquals(100, insets.getStableInsetBottom()); assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(Type.systemBars())); assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets()); @@ -115,7 +116,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, 0, 0, null); + false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null); assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets()); assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars())); assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars())); @@ -131,7 +132,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300)); mState.getSource(ITYPE_IME).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, null); + false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, 0, null); assertEquals(0, insets.getSystemWindowInsetBottom()); assertEquals(100, insets.getInsets(ime()).bottom); assertTrue(insets.isVisible(ime())); @@ -147,11 +148,28 @@ public class InsetsStateTest { mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300)); mState.getSource(ITYPE_IME).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, + false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, SYSTEM_UI_FLAG_LAYOUT_STABLE, null); assertEquals(100, insets.getSystemWindowInsetTop()); insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false, - DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, + DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, + 0 /* legacySystemUiFlags */, null); + assertEquals(0, insets.getSystemWindowInsetTop()); + } + } + + @Test + public void testCalculateInsets_systemUiFlagLayoutStable_windowFlagFullscreen() { + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(ITYPE_STATUS_BAR).setVisible(false); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, + false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN, + SYSTEM_UI_FLAG_LAYOUT_STABLE, null); + assertEquals(0, insets.getSystemWindowInsetTop()); + insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false, + DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, 0 /* legacySystemUiFlags */, null); assertEquals(0, insets.getSystemWindowInsetTop()); } @@ -195,7 +213,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, 0, 0, null); + false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null); assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets()); assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars())); assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars())); @@ -211,7 +229,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, 0, 0, null); + false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null); assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets()); assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars())); assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars())); @@ -226,7 +244,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_IME).setVisible(true); mState.removeSource(ITYPE_IME); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false, - DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null); + DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, null); assertEquals(0, insets.getSystemWindowInsetBottom()); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index c710bed29361..5cdcaba6ce48 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -160,6 +160,7 @@ applications that come with the platform <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/> <permission name="android.permission.DUMP"/> + <permission name="android.permission.HANDLE_CAR_MODE_CHANGES"/> <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.LOCAL_MAC_ADDRESS"/> <permission name="android.permission.MANAGE_USERS"/> @@ -426,6 +427,10 @@ applications that come with the platform <permission name="android.permission.CAPTURE_AUDIO_OUTPUT" /> <!-- Permissions required for CTS test - AdbManagerTest --> <permission name="android.permission.MANAGE_DEBUGGING" /> + <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp --> + <permission name="android.car.permission.CAR_DRIVING_STATE" /> + <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases --> + <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 5a50245a3765..67d8c07e61de 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -149,6 +149,9 @@ void DeferredLayerUpdater::apply() { sk_sp<SkImage> layerImage = mImageSlots[slot].createIfNeeded( hardwareBuffer, dataspace, newContent, mRenderState.getRenderThread().getGrContext()); + // unref to match the ref added by ASurfaceTexture_dequeueBuffer. eglCreateImageKHR + // (invoked by createIfNeeded) will add a ref to the AHardwareBuffer. + AHardwareBuffer_release(hardwareBuffer); if (layerImage.get()) { SkMatrix textureTransform; mat4(transformMatrix).copyTo(textureTransform); diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java index 496885cd1f37..6a689756a1c0 100644 --- a/location/java/android/location/GpsStatus.java +++ b/location/java/android/location/GpsStatus.java @@ -151,6 +151,15 @@ public final class GpsStatus { return status; } + /** + * Builds an empty GpsStatus. + * + * @hide + */ + static GpsStatus createEmpty() { + return new GpsStatus(); + } + private GpsStatus() { } diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 241e9399be0a..2a2aaea035ff 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1922,12 +1922,17 @@ public class LocationManager { GnssStatus gnssStatus = mGnssStatusListenerManager.getGnssStatus(); int ttff = mGnssStatusListenerManager.getTtff(); + + // even though this method is marked nullable, there are legacy applications that expect + // this to never return null, so avoid breaking those apps if (gnssStatus != null) { if (status == null) { status = GpsStatus.create(gnssStatus, ttff); } else { status.setStatus(gnssStatus, ttff); } + } else if (status == null) { + status = GpsStatus.createEmpty(); } return status; } diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index 590def4d4ced..98ca2f9c4253 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -34,7 +34,6 @@ import android.media.tv.tunerresourcemanager.ResourceClientProfile; import android.media.tv.tunerresourcemanager.TunerResourceManager; import android.os.Bundle; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.IHwBinder; import android.os.Looper; @@ -50,6 +49,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -392,7 +392,10 @@ public final class MediaCas implements AutoCloseable { @Override public void onReclaimResources() { synchronized (mSessionMap) { - mSessionMap.forEach((casSession, sessionResourceHandle) -> casSession.close()); + List<Session> sessionList = new ArrayList<>(mSessionMap.keySet()); + for (Session casSession: sessionList) { + casSession.close(); + } } mEventHandler.sendMessage(mEventHandler.obtainMessage( EventHandler.MSG_CAS_RESOURCE_LOST)); @@ -734,7 +737,7 @@ public final class MediaCas implements AutoCloseable { ResourceClientProfile profile = new ResourceClientProfile(tvInputServiceSessionId, priorityHint); mTunerResourceManager.registerClientProfile( - profile, new HandlerExecutor(mEventHandler), mResourceListener, clientId); + profile, context.getMainExecutor(), mResourceListener, clientId); mClientId = clientId[0]; } } diff --git a/media/java/android/media/MediaMetrics.java b/media/java/android/media/MediaMetrics.java index f6f482dd0cd3..eaf86bb3936c 100644 --- a/media/java/android/media/MediaMetrics.java +++ b/media/java/android/media/MediaMetrics.java @@ -53,6 +53,7 @@ public class MediaMetrics { public static final String AUDIO_SERVICE = AUDIO + SEPARATOR + "service"; public static final String AUDIO_VOLUME = AUDIO + SEPARATOR + "volume"; public static final String AUDIO_VOLUME_EVENT = AUDIO_VOLUME + SEPARATOR + "event"; + public static final String AUDIO_MODE = AUDIO + SEPARATOR + "mode"; } /** @@ -140,6 +141,10 @@ public class MediaMetrics { public static final Key<String> REQUEST = createKey("request", String.class); + // For audio mode + public static final Key<String> REQUESTED_MODE = + createKey("requestedMode", String.class); // audio_mode + // For Bluetooth public static final Key<String> SCO_AUDIO_MODE = createKey("scoAudioMode", String.class); diff --git a/packages/CarSystemUI/TEST_MAPPING b/packages/CarSystemUI/TEST_MAPPING index 6056ddfc0bc7..c18a12aeb2aa 100644 --- a/packages/CarSystemUI/TEST_MAPPING +++ b/packages/CarSystemUI/TEST_MAPPING @@ -8,5 +8,16 @@ } ] } + ], + + "carsysui-presubmit": [ + { + "name": "CarSystemUITests", + "options" : [ + { + "include-annotation": "com.android.systemui.car.CarSystemUiTest" + } + ] + } ] } diff --git a/packages/CarSystemUI/res/layout/system_icons.xml b/packages/CarSystemUI/res/layout/system_icons.xml index a7dd65eab550..d23579294ce8 100644 --- a/packages/CarSystemUI/res/layout/system_icons.xml +++ b/packages/CarSystemUI/res/layout/system_icons.xml @@ -31,10 +31,4 @@ android:gravity="center_vertical" android:orientation="horizontal" /> - - <com.android.systemui.BatteryMeterView - android:id="@+id/battery" - android:layout_width="0dp" - android:layout_height="match_parent" - /> </LinearLayout>
\ No newline at end of file diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java b/packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java new file mode 100644 index 000000000000..5f593b06c511 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.car; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotates that a test class should be run as part of CarSystemUI presubmit + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +public @interface CarSystemUiTest { +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java index 37dfce4e16ce..35b2080dddf9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java @@ -16,10 +16,12 @@ package com.android.systemui.car.navigationbar; +import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; import static android.view.InsetsState.ITYPE_CLIMATE_BAR; import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.InsetsState.ITYPE_TOP_GESTURES; import static android.view.InsetsState.containsType; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; @@ -368,13 +370,15 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, height, - WindowManager.LayoutParams.TYPE_STATUS_BAR, + WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, PixelFormat.TRANSLUCENT); lp.setTitle("TopCarNavigationBar"); + lp.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES}; + lp.setFitInsetsTypes(0); lp.windowAnimations = 0; lp.gravity = Gravity.TOP; mWindowManager.addView(mTopNavigationBarWindow, lp); @@ -388,13 +392,14 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, height, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, PixelFormat.TRANSLUCENT); lp.setTitle("BottomCarNavigationBar"); + lp.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR, ITYPE_BOTTOM_GESTURES}; lp.windowAnimations = 0; lp.gravity = Gravity.BOTTOM; mWindowManager.addView(mBottomNavigationBarWindow, lp); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java index 029d4c7fa2fb..0ced4021ce38 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java @@ -16,7 +16,10 @@ package com.android.systemui.car.navigationbar; +import static android.view.WindowInsets.Type.systemBars; + import android.content.Context; +import android.graphics.Insets; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -79,9 +82,28 @@ public class CarNavigationBarView extends LinearLayout { @Override public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) { + applyMargins(windowInsets.getInsets(systemBars())); return windowInsets; } + private void applyMargins(Insets insets) { + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getLayoutParams() instanceof LayoutParams) { + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.rightMargin != insets.right || lp.leftMargin != insets.left + || lp.topMargin != insets.top || lp.bottomMargin != insets.bottom) { + lp.rightMargin = insets.right; + lp.leftMargin = insets.left; + lp.topMargin = insets.top; + lp.bottomMargin = insets.bottom; + child.requestLayout(); + } + } + } + } + // Used to forward touch events even if the touch was initiated from a child component @Override public boolean onInterceptTouchEvent(MotionEvent ev) { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarStatusBarHeader.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarStatusBarHeader.java index bab67154e75d..0a677bfaa742 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarStatusBarHeader.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarStatusBarHeader.java @@ -26,7 +26,6 @@ import android.widget.LinearLayout; import androidx.annotation.IdRes; import com.android.settingslib.Utils; -import com.android.systemui.BatteryMeterView; import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; @@ -49,10 +48,7 @@ public class CarStatusBarHeader extends LinearLayout { float intensity = colorForeground == Color.WHITE ? 0f : 1f; Rect tintArea = new Rect(0, 0, 0, 0); - applyDarkness(R.id.battery, tintArea, intensity, colorForeground); applyDarkness(R.id.clock, tintArea, intensity, colorForeground); - - ((BatteryMeterView) findViewById(R.id.battery)).setForceShowPercent(true); } private void applyDarkness(@IdRes int id, Rect tintArea, float intensity, int color) { diff --git a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java index fe59cbf20a13..d769cacadf1d 100644 --- a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java +++ b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java @@ -33,6 +33,7 @@ import androidx.test.internal.runner.ClassPathScanner.ExternalClassNameFilter; import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import org.junit.Test; import org.junit.runner.RunWith; @@ -55,6 +56,7 @@ import java.util.Collections; * test suite causes errors, such as the incorrect settings provider being cached. * For an example, see {@link com.android.systemui.DependencyTest}. */ +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @SmallTest public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestCase { diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java index 7996170ba7d6..e179ef1ce2a4 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java @@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.CarSystemUiTest; import org.junit.Before; import org.junit.Test; @@ -39,6 +40,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java index 189e240169c3..62dc23624520 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java @@ -41,6 +41,7 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.car.window.OverlayViewGlobalStateController; import com.android.systemui.keyguard.DismissCallbackRegistry; @@ -59,6 +60,7 @@ import org.mockito.MockitoAnnotations; import dagger.Lazy; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java index a57736bb3502..4b8268052324 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java @@ -39,6 +39,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.tests.R; import org.junit.Before; @@ -49,6 +50,7 @@ import org.mockito.MockitoAnnotations; import java.util.List; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java index 893057e222a9..f623c26d12b6 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java @@ -28,6 +28,7 @@ import android.widget.LinearLayout; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.tests.R; import org.junit.Before; @@ -38,6 +39,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java index e84e42c77245..dec8b8ecdfb4 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java @@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.car.hvac.HvacController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -41,6 +42,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java index 0caa86f0eab2..d9edfa960858 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java @@ -45,6 +45,7 @@ import com.android.internal.view.AppearanceRegion; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.LightBarController; @@ -63,6 +64,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java index 19e394f69af4..47fd8201d197 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import org.junit.After; import org.junit.Before; @@ -38,6 +39,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java index bcaa5e9a03ee..173f5487c728 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java @@ -37,6 +37,7 @@ import android.widget.LinearLayout; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.statusbar.AlphaOptimizedImageView; import com.android.systemui.tests.R; @@ -45,6 +46,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java index ccaeb458fe54..384888ab42c3 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.car.window.OverlayViewGlobalStateController; import org.junit.Before; @@ -38,6 +39,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java index 89dac58cd2a7..d51aeb18135d 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java @@ -37,6 +37,7 @@ import com.android.car.notification.NotificationDataManager; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -49,6 +50,7 @@ import org.mockito.MockitoAnnotations; import java.util.Collections; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java index 77620f3fb345..421e2109356d 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java @@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.car.CarSystemUiTest; import org.junit.Before; import org.junit.Test; @@ -44,6 +45,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java index 73f9f6a55afc..20576e9ec11f 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java @@ -35,6 +35,7 @@ import android.view.DisplayInfo; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import org.junit.Before; import org.junit.Test; @@ -46,6 +47,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java index 797dbf515b7e..2e9d43b595a1 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java @@ -36,6 +36,7 @@ import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.car.window.OverlayViewGlobalStateController; import org.junit.Before; @@ -44,6 +45,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java index a808e2d40e26..de6feb64391f 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java @@ -25,6 +25,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.CarSystemUiTest; import org.junit.Before; import org.junit.Test; @@ -32,6 +33,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java index 232df2eced39..31f1170c9603 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java @@ -35,11 +35,13 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java index 45a05ac69bd7..7311cdb68a3c 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java @@ -39,6 +39,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.tests.R; @@ -52,6 +53,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java index c24a3b52e348..e784761f6d5d 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java @@ -29,6 +29,7 @@ import android.view.ViewGroup; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.tests.R; import org.junit.Before; @@ -39,6 +40,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java index cba42e5a9be4..20f9bc8ec1cb 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java @@ -32,6 +32,7 @@ import android.view.ViewStub; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.tests.R; @@ -44,6 +45,7 @@ import org.mockito.MockitoAnnotations; import java.util.Arrays; +@CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 570c2ab3efd1..3100e2fce085 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -317,6 +317,11 @@ <!-- Permissions required for CTS test - AdbManagerTest --> <uses-permission android:name="android.permission.MANAGE_DEBUGGING" /> + <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp --> + <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" /> + <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases --> + <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index 5c00af5705e9..436188a83d4f 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -62,7 +62,7 @@ android:gravity="center_vertical" android:focusable="true" android:singleLine="true" - android:ellipsize="end" + android:ellipsize="marquee" android:textAppearance="@style/TextAppearance.QS.Status" android:layout_marginEnd="4dp" android:visibility="gone"/> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index db45a60ab7c0..f51cffcdfb5c 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2404,17 +2404,26 @@ <!-- Title for notification & dialog that the user's phone last shut down because it got too hot. [CHAR LIMIT=40] --> <string name="thermal_shutdown_title">Phone turned off due to heat</string> - <!-- Message body for notification that user's phone last shut down because it got too hot. [CHAR LIMIT=100] --> - <string name="thermal_shutdown_message">Your phone is now running normally</string> - <!-- Text body for dialog alerting user that their phone last shut down because it got too hot. [CHAR LIMIT=450] --> + <!-- Message body for notification that user's phone last shut down because it got too hot. [CHAR LIMIT=120] --> + <string name="thermal_shutdown_message">Your phone is now running normally.\nTap for more info</string> + <!-- Text body for dialog alerting user that their phone last shut down because it got too hot. [CHAR LIMIT=500] --> <string name="thermal_shutdown_dialog_message">Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n\t• Use resource-intensive apps (such as gaming, video, or navigation apps)\n\t• Download or upload large files\n\t• Use your phone in high temperatures</string> + <!-- Text help link for care instructions for overheating devices [CHAR LIMIT=40] --> + <string name="thermal_shutdown_dialog_help_text">See care steps</string> + <!-- URL for care instructions for overheating devices --> + <string name="thermal_shutdown_dialog_help_url" translatable="false"></string> <!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] --> <string name="high_temp_title">Phone is getting warm</string> - <!-- Message body for notification that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=100] --> - <string name="high_temp_notif_message">Some features limited while phone cools down</string> - <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=300] --> + <!-- Message body for notification that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=120] --> + <string name="high_temp_notif_message">Some features limited while phone cools down.\nTap for more info</string> + <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=350] --> <string name="high_temp_dialog_message">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string> + <!-- Text help link for care instructions for overheating devices [CHAR LIMIT=40] --> + <string name="high_temp_dialog_help_text">See care steps</string> + <!-- URL for care instructions for overheating devices --> + <string name="high_temp_dialog_help_url" translatable="false"></string> + <!-- Title for alarm dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=30] --> <string name="high_temp_alarm_title">Unplug charger</string> <!-- Text body for dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=300] --> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 6f103b020814..e252195da136 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -207,12 +207,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi /** Whether or not the BubbleStackView has been added to the WindowManager. */ private boolean mAddedToWindowManager = false; - /** - * Value from {@link NotificationShadeWindowController#getForceHasTopUi()} when we forced top UI - * due to expansion. We'll restore this value when the stack collapses. - */ - private boolean mHadTopUi = false; - // Listens to user switch so bubbles can be saved and restored. private final NotificationLockscreenUserManager mNotifUserManager; @@ -1303,7 +1297,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // Collapsing? Do this first before remaining steps. if (update.expandedChanged && !update.expanded) { mStackView.setExpanded(false); - mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi); + mNotificationShadeWindowController.setRequestTopUi(false, TAG); } // Do removals, if any. @@ -1393,8 +1387,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (update.expandedChanged && update.expanded) { if (mStackView != null) { mStackView.setExpanded(true); - mHadTopUi = mNotificationShadeWindowController.getForceHasTopUi(); - mNotificationShadeWindowController.setForceHasTopUi(true); + mNotificationShadeWindowController.setRequestTopUi(true, TAG); } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index b2e91643bed2..ef51abb1404d 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -2129,7 +2129,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private boolean mShowing; private float mScrimAlpha; private ResetOrientationData mResetOrientationData; - private boolean mHadTopUi; private final NotificationShadeWindowController mNotificationShadeWindowController; private final NotificationShadeDepthController mDepthController; private final SysUiState mSysUiState; @@ -2397,8 +2396,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, public void show() { super.show(); mShowing = true; - mHadTopUi = mNotificationShadeWindowController.getForceHasTopUi(); - mNotificationShadeWindowController.setForceHasTopUi(true); + mNotificationShadeWindowController.setRequestTopUi(true, TAG); mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true) .commitUpdate(mContext.getDisplayId()); @@ -2499,7 +2497,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, dismissOverflow(true); dismissPowerOptions(true); if (mControlsUiController != null) mControlsUiController.hide(); - mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi); + mNotificationShadeWindowController.setRequestTopUi(false, TAG); mDepthController.updateGlobalDialogVisibility(0, null /* view */); mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false) .commitUpdate(mContext.getDisplayId()); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java index ead17867844a..72019315139b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -21,7 +21,6 @@ import android.animation.Animator; import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.annotation.IntDef; -import android.content.Context; import android.graphics.Rect; import android.view.SurfaceControl; @@ -56,13 +55,15 @@ public class PipAnimationController { public static final int TRANSITION_DIRECTION_TO_PIP = 2; public static final int TRANSITION_DIRECTION_TO_FULLSCREEN = 3; public static final int TRANSITION_DIRECTION_TO_SPLIT_SCREEN = 4; + public static final int TRANSITION_DIRECTION_REMOVE_STACK = 5; @IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = { TRANSITION_DIRECTION_NONE, TRANSITION_DIRECTION_SAME, TRANSITION_DIRECTION_TO_PIP, TRANSITION_DIRECTION_TO_FULLSCREEN, - TRANSITION_DIRECTION_TO_SPLIT_SCREEN + TRANSITION_DIRECTION_TO_SPLIT_SCREEN, + TRANSITION_DIRECTION_REMOVE_STACK }) @Retention(RetentionPolicy.SOURCE) public @interface TransitionDirection {} @@ -88,7 +89,7 @@ public class PipAnimationController { }); @Inject - PipAnimationController(Context context, PipSurfaceTransactionHelper helper) { + PipAnimationController(PipSurfaceTransactionHelper helper) { mSurfaceTransactionHelper = helper; } @@ -338,6 +339,10 @@ public class PipAnimationController { @Override void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) { + if (getTransitionDirection() == TRANSITION_DIRECTION_REMOVE_STACK) { + // while removing the pip stack, no extra work needs to be done here. + return; + } getSurfaceTransactionHelper() .resetScale(tx, leash, getDestinationBounds()) .crop(tx, leash, getDestinationBounds()) diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 0141dee04086..c4152fa9a4e7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE; +import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_SAME; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; @@ -337,23 +338,32 @@ public class PipTaskOrganizer extends TaskOrganizer implements + " mInPip=" + mInPip + " mExitingPip=" + mExitingPip + " mToken=" + mToken); return; } - getUpdateHandler().post(() -> { - try { - // Reset the task bounds first to ensure the activity configuration is reset as well - final WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.setBounds(mToken, null); - WindowOrganizer.applyTransaction(wct); - - ActivityTaskManager.getService().removeStacksInWindowingModes( - new int[]{ WINDOWING_MODE_PINNED }); - } catch (RemoteException e) { - Log.e(TAG, "Failed to remove PiP", e); - } - }); + + // removePipImmediately is expected when the following animation finishes. + mUpdateHandler.post(() -> mPipAnimationController + .getAnimator(mLeash, mLastReportedBounds, 1f, 0f) + .setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(mEnterExitAnimationDuration) + .start()); mInitialState.remove(mToken.asBinder()); mExitingPip = true; } + private void removePipImmediately() { + try { + // Reset the task bounds first to ensure the activity configuration is reset as well + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mToken, null); + WindowOrganizer.applyTransaction(wct); + + ActivityTaskManager.getService().removeStacksInWindowingModes( + new int[]{ WINDOWING_MODE_PINNED }); + } catch (RemoteException e) { + Log.e(TAG, "Failed to remove PiP", e); + } + } + @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { Objects.requireNonNull(info, "Requires RunningTaskInfo"); @@ -803,7 +813,10 @@ public class PipTaskOrganizer extends TaskOrganizer implements + "directly"); } mLastReportedBounds.set(destinationBounds); - if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) { + if (direction == TRANSITION_DIRECTION_REMOVE_STACK) { + removePipImmediately(); + return; + } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 10b04c0129e4..6abbbbeaa397 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -24,6 +24,7 @@ import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioAttributes; @@ -376,13 +377,15 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { return; } mHighTempWarning = true; + final String message = mContext.getString(R.string.high_temp_notif_message); final Notification.Builder nb = new Notification.Builder(mContext, NotificationChannels.ALERTS) .setSmallIcon(R.drawable.ic_device_thermostat_24) .setWhen(0) .setShowWhen(false) .setContentTitle(mContext.getString(R.string.high_temp_title)) - .setContentText(mContext.getString(R.string.high_temp_notif_message)) + .setContentText(message) + .setStyle(new Notification.BigTextStyle().bigText(message)) .setVisibility(Notification.VISIBILITY_PUBLIC) .setContentIntent(pendingBroadcast(ACTION_CLICKED_TEMP_WARNING)) .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING)) @@ -402,6 +405,23 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { d.setPositiveButton(com.android.internal.R.string.ok, null); d.setShowForAllUsers(true); d.setOnDismissListener(dialog -> mHighTempDialog = null); + final String url = mContext.getString(R.string.high_temp_dialog_help_url); + if (!url.isEmpty()) { + d.setNeutralButton(R.string.high_temp_dialog_help_text, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final Intent helpIntent = + new Intent(Intent.ACTION_VIEW) + .setData(Uri.parse(url)) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Dependency.get(ActivityStarter.class).startActivity(helpIntent, + true /* dismissShade */, resultCode -> { + mHighTempDialog = null; + }); + } + }); + } d.show(); mHighTempDialog = d; } @@ -420,19 +440,38 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { d.setPositiveButton(com.android.internal.R.string.ok, null); d.setShowForAllUsers(true); d.setOnDismissListener(dialog -> mThermalShutdownDialog = null); + final String url = mContext.getString(R.string.thermal_shutdown_dialog_help_url); + if (!url.isEmpty()) { + d.setNeutralButton(R.string.thermal_shutdown_dialog_help_text, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final Intent helpIntent = + new Intent(Intent.ACTION_VIEW) + .setData(Uri.parse(url)) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Dependency.get(ActivityStarter.class).startActivity(helpIntent, + true /* dismissShade */, resultCode -> { + mThermalShutdownDialog = null; + }); + } + }); + } d.show(); mThermalShutdownDialog = d; } @Override public void showThermalShutdownWarning() { + final String message = mContext.getString(R.string.thermal_shutdown_message); final Notification.Builder nb = new Notification.Builder(mContext, NotificationChannels.ALERTS) .setSmallIcon(R.drawable.ic_device_thermostat_24) .setWhen(0) .setShowWhen(false) .setContentTitle(mContext.getString(R.string.thermal_shutdown_title)) - .setContentText(mContext.getString(R.string.thermal_shutdown_message)) + .setContentText(message) + .setStyle(new Notification.BigTextStyle().bigText(message)) .setVisibility(Notification.VISIBILITY_PUBLIC) .setContentIntent(pendingBroadcast(ACTION_CLICKED_THERMAL_SHUTDOWN_WARNING)) .setDeleteIntent( diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index c4bb4e86e41e..6e4ab9a3323a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -78,6 +78,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, private SettingsButton mSettingsButton; protected View mSettingsContainer; private PageIndicator mPageIndicator; + private TextView mBuildText; + private boolean mShouldShowBuildText; private boolean mQsDisabled; private QSPanel mQsPanel; @@ -147,6 +149,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mActionsContainer = findViewById(R.id.qs_footer_actions_container); mEditContainer = findViewById(R.id.qs_footer_actions_edit_container); + mBuildText = findViewById(R.id.build); // RenderThread is doing more harm than good when touching the header (to expand quick // settings), so disable it for this view @@ -162,16 +165,19 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, } private void setBuildText() { - TextView v = findViewById(R.id.build); - if (v == null) return; + if (mBuildText == null) return; if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) { - v.setText(mContext.getString( + mBuildText.setText(mContext.getString( com.android.internal.R.string.bugreport_status, Build.VERSION.RELEASE_OR_CODENAME, Build.ID)); - v.setVisibility(View.VISIBLE); + // Set as selected for marquee before its made visible, then it won't be announced when + // it's made visible. + mBuildText.setSelected(true); + mShouldShowBuildText = true; } else { - v.setVisibility(View.GONE); + mShouldShowBuildText = false; + mBuildText.setSelected(false); } } @@ -321,6 +327,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.INVISIBLE); mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE); mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE); + + mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.GONE); } private boolean showUserSwitcher() { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index d9d46605f209..570a4bb3cd12 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -197,7 +197,9 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, return; } mView.setHidden(mKeyguardStateController.isShowing()); - mImePositionProcessor.updateAdjustForIme(); + if (!mKeyguardStateController.isShowing()) { + mImePositionProcessor.updateAdjustForIme(); + } } @Override @@ -316,7 +318,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } private void update(Configuration configuration) { - final boolean isDividerHidden = mView != null && mView.isHidden(); + final boolean isDividerHidden = mView != null && mKeyguardStateController.isShowing(); removeDivider(); addDivider(configuration); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java index 89f4a94ac877..5aeca5e07bdd 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java @@ -138,18 +138,20 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor } @Override - public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop, - boolean imeShouldShow, SurfaceControl.Transaction t) { + @ImeAnimationFlags + public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean imeShouldShow, boolean imeIsFloating, SurfaceControl.Transaction t) { mHiddenTop = hiddenTop; mShownTop = shownTop; mTargetShown = imeShouldShow; if (!isDividerVisible()) { - return; + return 0; } final boolean splitIsVisible = !getView().isHidden(); mSecondaryHasFocus = getSecondaryHasFocus(displayId); final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus - && !getLayout().mDisplayLayout.isLandscape() && !mSplits.mDivider.isMinimized(); + && !imeIsFloating && !getLayout().mDisplayLayout.isLandscape() + && !mSplits.mDivider.isMinimized(); if (mLastAdjustTop < 0) { mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop; } else if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) { @@ -167,7 +169,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor if (mPaused) { mPausedTargetAdjusted = targetAdjusted; if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState()); - return; + return (targetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0; } mTargetAdjusted = targetAdjusted; updateDimTargets(); @@ -186,6 +188,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor } else { mAdjustedWhileHidden = true; } + return (mTargetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0; } private void updateImeAdjustState() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index c87b9986ca55..91cc8963b947 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -41,8 +41,8 @@ import com.android.systemui.statusbar.notification.row.StackScrollerDecorView import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.children -import com.android.systemui.util.takeUntil import com.android.systemui.util.foldToSparseArray +import com.android.systemui.util.takeUntil import javax.inject.Inject /** @@ -166,6 +166,9 @@ class NotificationSectionsManager @Inject internal constructor( peopleHubSubscription?.unsubscribe() peopleHubSubscription = null peopleHeaderView = reinflateView(peopleHeaderView, layoutInflater, R.layout.people_strip) + .apply { + setOnHeaderClickListener(View.OnClickListener { onGentleHeaderClick() }) + } if (ENABLE_SNOOZED_CONVERSATION_HUB) { peopleHubSubscription = peopleHubViewAdapter.bindView(peopleHubViewBoundary) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt index 8f77a1d776e4..b13e7fb839ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt @@ -93,6 +93,8 @@ class PeopleHubView(context: Context, attrs: AttributeSet) : } } + fun setOnHeaderClickListener(listener: OnClickListener) = label.setOnClickListener(listener) + private inner class PersonDataListenerImpl(val avatarView: ImageView) : DataListener<PersonViewModel?> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java index 5164440c1463..bc73be19ab59 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java @@ -61,6 +61,8 @@ import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import java.util.function.Consumer; import javax.inject.Inject; @@ -432,7 +434,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, } private void applyHasTopUi(State state) { - mHasTopUiChanged = state.mForceHasTopUi || isExpanded(state); + mHasTopUiChanged = !state.mComponentsForcingTopUi.isEmpty() || isExpanded(state); } private void applyNotTouchable(State state) { @@ -635,12 +637,17 @@ public class NotificationShadeWindowController implements Callback, Dumpable, apply(mCurrentState); } - public boolean getForceHasTopUi() { - return mCurrentState.mForceHasTopUi; - } - - public void setForceHasTopUi(boolean forceHasTopUi) { - mCurrentState.mForceHasTopUi = forceHasTopUi; + /** + * SystemUI may need top-ui to avoid jank when performing animations. After the + * animation is performed, the component should remove itself from the list of features that + * are forcing SystemUI to be top-ui. + */ + public void setRequestTopUi(boolean requestTopUi, String componentTag) { + if (requestTopUi) { + mCurrentState.mComponentsForcingTopUi.add(componentTag); + } else { + mCurrentState.mComponentsForcingTopUi.remove(componentTag); + } apply(mCurrentState); } @@ -663,7 +670,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, boolean mBackdropShowing; boolean mWallpaperSupportsAmbientMode; boolean mNotTouchable; - boolean mForceHasTopUi; + Set<String> mComponentsForcingTopUi = new HashSet<>(); /** * The {@link StatusBar} state from the status bar. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index c14a5ef328aa..b65bd45ce11c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -213,7 +213,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule; import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -2397,18 +2396,31 @@ public class StatusBar extends SystemUI implements DemoMode, new WirelessChargingAnimation.Callback() { @Override public void onAnimationStarting() { + mNotificationShadeWindowController.setRequestTopUi(true, TAG); CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1); } @Override public void onAnimationEnded() { CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView()); + mNotificationShadeWindowController.setRequestTopUi(false, TAG); } }, mDozing).show(animationDelay); } else { // workspace WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null, - transmittingBatteryLevel, batteryLevel, null, false).show(animationDelay); + transmittingBatteryLevel, batteryLevel, + new WirelessChargingAnimation.Callback() { + @Override + public void onAnimationStarting() { + mNotificationShadeWindowController.setRequestTopUi(true, TAG); + } + + @Override + public void onAnimationEnded() { + mNotificationShadeWindowController.setRequestTopUi(false, TAG); + } + }, false).show(animationDelay); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 18a7adda3f7d..cf83603997c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -284,6 +284,9 @@ public class MobileSignalController extends SignalController< mNetworkToIconLookup.put(toDisplayIconKey( TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE), TelephonyIcons.NR_5G_PLUS); + mNetworkToIconLookup.put(toIconKey( + TelephonyManager.NETWORK_TYPE_NR), + TelephonyIcons.NR_5G); } private String getIconKey() { @@ -306,9 +309,9 @@ public class MobileSignalController extends SignalController< case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO: return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA_Plus"; case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA: - return "5G"; + return toIconKey(TelephonyManager.NETWORK_TYPE_NR); case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE: - return "5G_Plus"; + return toIconKey(TelephonyManager.NETWORK_TYPE_NR) + "_Plus"; default: return "unsupported"; } diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index 3402a52f056a..89f469a438a9 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -19,6 +19,7 @@ package com.android.systemui.wm; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.annotation.IntDef; import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; @@ -136,12 +137,16 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } - private void dispatchStartPositioning(int displayId, int hiddenTop, int shownTop, - boolean show, SurfaceControl.Transaction t) { + @ImePositionProcessor.ImeAnimationFlags + private int dispatchStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean show, boolean isFloating, SurfaceControl.Transaction t) { synchronized (mPositionProcessors) { + int flags = 0; for (ImePositionProcessor pp : mPositionProcessors) { - pp.onImeStartPositioning(displayId, hiddenTop, shownTop, show, t); + flags |= pp.onImeStartPositioning( + displayId, hiddenTop, shownTop, show, isFloating, t); } + return flags; } } @@ -184,6 +189,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged int mRotation = Surface.ROTATION_0; boolean mImeShowing = false; final Rect mImeFrame = new Rect(); + boolean mAnimateAlpha = true; PerDisplay(int displayId, int initialRotation) { mDisplayId = displayId; @@ -273,15 +279,29 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return mImeFrame.top + (int) surfaceOffset; } + private boolean calcIsFloating(InsetsSource imeSource) { + final Rect frame = imeSource.getFrame(); + if (frame.height() == 0) { + return true; + } + // Some Floating Input Methods will still report a frame, but the frame is actually + // a nav-bar inset created by WM and not part of the IME (despite being reported as + // an IME inset). For now, we assume that no non-floating IME will be <= this nav bar + // frame height so any reported frame that is <= nav-bar frame height is assumed to + // be floating. + return frame.height() <= mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId) + .navBarFrameHeight(); + } + private void startAnimation(final boolean show, final boolean forceRestart) { final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME); if (imeSource == null || mImeSourceControl == null) { return; } final Rect newFrame = imeSource.getFrame(); - final boolean isFloating = newFrame.height() == 0 && show; + final boolean isFloating = calcIsFloating(imeSource) && show; if (isFloating) { - // This is likely a "floating" or "expanded" IME, so to get animations, just + // This is a "floating" or "expanded" IME, so to get animations, just // pretend the ime has some size just below the screen. mImeFrame.set(newFrame); final int floatingInset = (int) ( @@ -334,7 +354,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged SurfaceControl.Transaction t = mTransactionPool.acquire(); float value = (float) animation.getAnimatedValue(); t.setPosition(mImeSourceControl.getLeash(), x, value); - final float alpha = isFloating ? (value - hiddenY) / (shownY - hiddenY) : 1.f; + final float alpha = (mAnimateAlpha || isFloating) + ? (value - hiddenY) / (shownY - hiddenY) : 1.f; t.setAlpha(mImeSourceControl.getLeash(), alpha); dispatchPositionChanged(mDisplayId, imeTop(value), t); t.apply(); @@ -347,16 +368,18 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged public void onAnimationStart(Animator animation) { SurfaceControl.Transaction t = mTransactionPool.acquire(); t.setPosition(mImeSourceControl.getLeash(), x, startY); - final float alpha = isFloating ? (startY - hiddenY) / (shownY - hiddenY) : 1.f; - t.setAlpha(mImeSourceControl.getLeash(), alpha); if (DEBUG) { Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:" + imeTop(hiddenY) + "->" + imeTop(shownY) + " showing:" + (mAnimationDirection == DIRECTION_SHOW)); } - dispatchStartPositioning(mDisplayId, imeTop(hiddenY), - imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, - t); + int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY), + imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t); + mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0; + final float alpha = (mAnimateAlpha || isFloating) + ? (startY - hiddenY) / (shownY - hiddenY) + : 1.f; + t.setAlpha(mImeSourceControl.getLeash(), alpha); if (mAnimationDirection == DIRECTION_SHOW) { t.show(mImeSourceControl.getLeash()); } @@ -419,15 +442,33 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged */ public interface ImePositionProcessor { /** + * Indicates that ime shouldn't animate alpha. It will always be opaque. Used when stuff + * behind the IME shouldn't be visible (for example during split-screen adjustment where + * there is nothing behind the ime). + */ + int IME_ANIMATION_NO_ALPHA = 1; + + /** @hide */ + @IntDef(prefix = { "IME_ANIMATION_" }, value = { + IME_ANIMATION_NO_ALPHA, + }) + @interface ImeAnimationFlags {} + + /** * Called when the IME position is starting to animate. * * @param hiddenTop The y position of the top of the IME surface when it is hidden. * @param shownTop The y position of the top of the IME surface when it is shown. * @param showing {@code true} when we are animating from hidden to shown, {@code false} * when animating from shown to hidden. + * @param isFloating {@code true} when the ime is a floating ime (doesn't inset). + * @return flags that may alter how ime itself is animated (eg. no-alpha). */ - default void onImeStartPositioning(int displayId, int hiddenTop, int shownTop, - boolean showing, SurfaceControl.Transaction t) {} + @ImeAnimationFlags + default int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean showing, boolean isFloating, SurfaceControl.Transaction t) { + return 0; + } /** * Called when the ime position changed. This is expected to be a synchronous call on the diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java index cfec1c07ff1d..a341f3050ea6 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java @@ -79,6 +79,7 @@ public class DisplayLayout { private final Rect mStableInsets = new Rect(); private boolean mHasNavigationBar = false; private boolean mHasStatusBar = false; + private int mNavBarFrameHeight = 0; /** * Create empty layout. @@ -146,6 +147,7 @@ public class DisplayLayout { if (mHasStatusBar) { convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar); } + mNavBarFrameHeight = getNavigationBarFrameHeight(res, mWidth > mHeight); } /** @@ -214,6 +216,11 @@ public class DisplayLayout { return mWidth > mHeight; } + /** Get the navbar frame height (used by ime). */ + public int navBarFrameHeight() { + return mNavBarFrameHeight; + } + /** Gets the orientation of this layout */ public int getOrientation() { return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; @@ -483,6 +490,7 @@ public class DisplayLayout { } else { return res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode); } + } else { if (navBarSide == NAV_BAR_BOTTOM) { return res.getDimensionPixelSize(landscape @@ -493,4 +501,11 @@ public class DisplayLayout { } } } + + /** @see com.android.server.wm.DisplayPolicy#getNavigationBarFrameHeight */ + public static int getNavigationBarFrameHeight(Resources res, boolean landscape) { + return res.getDimensionPixelSize(landscape + ? R.dimen.navigation_bar_frame_height_landscape + : R.dimen.navigation_bar_frame_height); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java index 536cae4380c4..c9c111198f4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java @@ -61,8 +61,7 @@ public class PipAnimationControllerTest extends SysuiTestCase { @Before public void setUp() throws Exception { mPipAnimationController = new PipAnimationController( - mContext, new PipSurfaceTransactionHelper(mContext, - mock(ConfigurationController.class))); + new PipSurfaceTransactionHelper(mContext, mock(ConfigurationController.class))); mLeash = new SurfaceControl.Builder() .setContainerLayer() .setName("FakeLeash") diff --git a/packages/WAPPushManager/AndroidManifest.xml b/packages/WAPPushManager/AndroidManifest.xml index 14e6e91e3a25..a75fb2d47bbd 100644 --- a/packages/WAPPushManager/AndroidManifest.xml +++ b/packages/WAPPushManager/AndroidManifest.xml @@ -23,6 +23,8 @@ <permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND" android:protectionLevel="signatureOrSystem" /> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> + <original-package android:name="com.android.smspush" /> <application android:allowClearUserData="false"> diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java index 19248ca54611..c25dd37bc7d9 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java @@ -31,9 +31,16 @@ import com.android.server.inputmethod.InputMethodManagerInternal; import java.util.Optional; import java.util.function.Consumer; - /** - * Controls the interaction with the IME for the inline suggestion sessions. + * Controls the interaction with the IME for the {@link AutofillInlineSuggestionsRequestSession}s. + * + * <p>The class maintains the inline suggestion session with the autofill service. There is at most + * one active inline suggestion session at any given corresponding to one focused view. + * New sessions are created only when {@link #onCreateInlineSuggestionsRequestLocked} is called.</p> + * + * <p>The class manages the interaction between the {@link com.android.server.autofill.Session} and + * the inline suggestion session whenever inline suggestions can be provided. All calls to the + * inline suggestion session must be made through this controller.</p> */ final class AutofillInlineSessionController { @NonNull @@ -66,7 +73,6 @@ final class AutofillInlineSessionController { mUiCallback = callback; } - /** * Requests the IME to create an {@link InlineSuggestionsRequest} for {@code autofillId}. * diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java index b2daae48bb0e..84fbe9a75a18 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java @@ -364,6 +364,9 @@ final class AutofillInlineSuggestionsRequestSession { } } + /** + * Internal implementation of {@link IInlineSuggestionsRequestCallback}. + */ private static final class InlineSuggestionsRequestCallbackImpl extends IInlineSuggestionsRequestCallback.Stub { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 56953708e383..3f867f656c24 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -18,7 +18,6 @@ package com.android.server.am; import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; import static android.os.Process.NFC_UID; import static android.os.Process.ROOT_UID; @@ -1361,13 +1360,12 @@ public final class ActiveServices { + String.format("0x%08X", manifestType) + " in service element of manifest file"); } - if ((foregroundServiceType & FOREGROUND_SERVICE_TYPE_LOCATION) != 0 - && !r.mAllowWhileInUsePermissionInFgs) { - // If the foreground service is not started from TOP process, do not allow it to - // have location capability, this prevents BG started FGS to have while-in-use - // location permission. + // If the foreground service is not started from TOP process, do not allow it to + // have while-in-use location/camera/microphone access. + if (!r.mAllowWhileInUsePermissionInFgs) { Slog.w(TAG, - "BG started FGS can not have location capability: service " + "Foreground service started from background can not have " + + "location/camera/microphone access: service " + r.shortInstanceName); } } diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java index 02fb34e73f7f..374c215fc6d0 100644 --- a/services/core/java/com/android/server/am/AppExitInfoTracker.java +++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java @@ -522,7 +522,7 @@ public final class AppExitInfoTracker { AppExitInfoContainer container = records.get(filterUid); if (container != null) { mTmpInfoList.clear(); - results.addAll(container.toListLocked(mTmpInfoList, filterPid)); + list.addAll(container.toListLocked(mTmpInfoList, filterPid)); } return AppExitInfoTracker.FOREACH_ACTION_NONE; }); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 5e908b26fafa..c4eca605206d 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -283,6 +283,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_HDMI_VOLUME_CHECK = 28; private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29; private static final int MSG_BROADCAST_MICROPHONE_MUTE = 30; + private static final int MSG_CHECK_MODE_FOR_UID = 31; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -3679,12 +3680,14 @@ public class AudioService extends IAudioService.Stub private final IBinder mCb; // To be notified of client's death private final int mPid; private final int mUid; + private String mPackage; private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client - SetModeDeathHandler(IBinder cb, int pid, int uid) { + SetModeDeathHandler(IBinder cb, int pid, int uid, String caller) { mCb = cb; mPid = pid; mUid = uid; + mPackage = caller; } public void binderDied() { @@ -3722,6 +3725,10 @@ public class AudioService extends IAudioService.Stub public int getUid() { return mUid; } + + public String getPackage() { + return mPackage; + } } /** @see AudioManager#setMode(int) */ @@ -3803,6 +3810,9 @@ public class AudioService extends IAudioService.Stub hdlr = h; // Remove from client list so that it is re-inserted at top of list iter.remove(); + if (hdlr.getMode() == AudioSystem.MODE_IN_COMMUNICATION) { + mAudioHandler.removeEqualMessages(MSG_CHECK_MODE_FOR_UID, hdlr); + } try { hdlr.getBinder().unlinkToDeath(hdlr, 0); if (cb != hdlr.getBinder()) { @@ -3833,7 +3843,7 @@ public class AudioService extends IAudioService.Stub } } else { if (hdlr == null) { - hdlr = new SetModeDeathHandler(cb, pid, uid); + hdlr = new SetModeDeathHandler(cb, pid, uid, caller); } // Register for client death notification try { @@ -3880,6 +3890,7 @@ public class AudioService extends IAudioService.Stub // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL mModeLogger.log( new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode)); + int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); int device = getDeviceForStream(streamType); int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device); @@ -3890,6 +3901,16 @@ public class AudioService extends IAudioService.Stub // change of mode may require volume to be re-applied on some devices updateAbsVolumeMultiModeDevices(oldMode, actualMode); + + if (actualMode == AudioSystem.MODE_IN_COMMUNICATION) { + sendMsg(mAudioHandler, + MSG_CHECK_MODE_FOR_UID, + SENDMSG_QUEUE, + 0, + 0, + hdlr, + CHECK_MODE_FOR_UID_PERIOD_MS); + } } return newModeOwnerPid; } @@ -6374,6 +6395,35 @@ public class AudioService extends IAudioService.Stub case MSG_BROADCAST_MICROPHONE_MUTE: mSystemServer.sendMicrophoneMuteChangedIntent(); break; + + case MSG_CHECK_MODE_FOR_UID: + synchronized (mDeviceBroker.mSetModeLock) { + if (msg.obj == null) { + break; + } + // If the app corresponding to this mode death handler object is not + // capturing or playing audio anymore after 3 seconds, remove it + // from the stack. Otherwise, check again in 3 seconds. + SetModeDeathHandler h = (SetModeDeathHandler) msg.obj; + if (mSetModeDeathHandlers.indexOf(h) < 0) { + break; + } + if (mRecordMonitor.isRecordingActiveForUid(h.getUid()) + || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) { + sendMsg(mAudioHandler, + MSG_CHECK_MODE_FOR_UID, + SENDMSG_QUEUE, + 0, + 0, + h, + CHECK_MODE_FOR_UID_PERIOD_MS); + break; + } + // For now just log the fact that an app is hogging the audio mode. + // TODO(b/160260850): remove abusive app from audio mode stack. + mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); + } + break; } } } @@ -7017,6 +7067,8 @@ public class AudioService extends IAudioService.Stub private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed + // check playback or record activity every 3 seconds for UIDs owning mode IN_COMMUNICATION + private static final int CHECK_MODE_FOR_UID_PERIOD_MS = 3000; private int safeMediaVolumeIndex(int device) { if (!mSafeMediaVolumeDevices.contains(device)) { diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index f3ff02f3aedc..0eb5a5d1fb48 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -27,28 +27,82 @@ import com.android.server.audio.AudioDeviceInventory.WiredDeviceConnectionState; public class AudioServiceEvents { final static class PhoneStateEvent extends AudioEventLogger.Event { + static final int MODE_SET = 0; + static final int MODE_IN_COMMUNICATION_TIMEOUT = 1; + + final int mOp; final String mPackage; final int mOwnerPid; final int mRequesterPid; final int mRequestedMode; final int mActualMode; + /** used for MODE_SET */ PhoneStateEvent(String callingPackage, int requesterPid, int requestedMode, int ownerPid, int actualMode) { + mOp = MODE_SET; mPackage = callingPackage; mRequesterPid = requesterPid; mRequestedMode = requestedMode; mOwnerPid = ownerPid; mActualMode = actualMode; + logMetricEvent(); + } + + /** used for MODE_IN_COMMUNICATION_TIMEOUT */ + PhoneStateEvent(String callingPackage, int ownerPid) { + mOp = MODE_IN_COMMUNICATION_TIMEOUT; + mPackage = callingPackage; + mOwnerPid = ownerPid; + mRequesterPid = 0; + mRequestedMode = 0; + mActualMode = 0; + logMetricEvent(); } @Override public String eventToString() { - return new StringBuilder("setMode(").append(AudioSystem.modeToString(mRequestedMode)) - .append(") from package=").append(mPackage) - .append(" pid=").append(mRequesterPid) - .append(" selected mode=").append(AudioSystem.modeToString(mActualMode)) - .append(" by pid=").append(mOwnerPid).toString(); + switch (mOp) { + case MODE_SET: + return new StringBuilder("setMode(") + .append(AudioSystem.modeToString(mRequestedMode)) + .append(") from package=").append(mPackage) + .append(" pid=").append(mRequesterPid) + .append(" selected mode=") + .append(AudioSystem.modeToString(mActualMode)) + .append(" by pid=").append(mOwnerPid).toString(); + case MODE_IN_COMMUNICATION_TIMEOUT: + return new StringBuilder("mode IN COMMUNICATION timeout") + .append(" for package=").append(mPackage) + .append(" pid=").append(mOwnerPid).toString(); + default: return new StringBuilder("FIXME invalid op:").append(mOp).toString(); + } + } + + /** + * Audio Analytics unique Id. + */ + private static final String mMetricsId = MediaMetrics.Name.AUDIO_MODE; + + private void logMetricEvent() { + switch (mOp) { + case MODE_SET: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "set") + .set(MediaMetrics.Property.REQUESTED_MODE, + AudioSystem.modeToString(mRequestedMode)) + .set(MediaMetrics.Property.MODE, AudioSystem.modeToString(mActualMode)) + .set(MediaMetrics.Property.CALLING_PACKAGE, mPackage) + .record(); + return; + case MODE_IN_COMMUNICATION_TIMEOUT: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "inCommunicationTimeout") + .set(MediaMetrics.Property.CALLING_PACKAGE, mPackage) + .record(); + return; + default: return; + } } } diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 98f409ea98e7..a5778836aa6e 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -366,6 +366,23 @@ public final class PlaybackActivityMonitor releasePlayer(piid, 0); } + /** + * Returns true if a player belonging to the app with given uid is active. + * + * @param uid the app uid + * @return true if a player is active, false otherwise + */ + public boolean isPlaybackActiveForUid(int uid) { + synchronized (mPlayerLock) { + for (AudioPlaybackConfiguration apc : mPlayers.values()) { + if (apc.isActive() && apc.getClientUid() == uid) { + return true; + } + } + } + return false; + } + protected void dump(PrintWriter pw) { // players pw.println("\nPlaybackActivityMonitor dump time: " diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java index 32c6cc32a78d..ea0107ecfd23 100644 --- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java +++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java @@ -215,6 +215,25 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin dispatchCallbacks(updateSnapshot(AudioManager.RECORD_CONFIG_EVENT_RELEASE, riid, null)); } + /** + * Returns true if a recorder belonging to the app with given uid is active. + * + * @param uid the app uid + * @return true if a recorder is active, false otherwise + */ + public boolean isRecordingActiveForUid(int uid) { + synchronized (mRecordStates) { + for (RecordingState state : mRecordStates) { + // Note: isActiveConfiguration() == true => state.getConfig() != null + if (state.isActiveConfiguration() + && state.getConfig().getClientUid() == uid) { + return true; + } + } + } + return false; + } + private void dispatchCallbacks(List<AudioRecordingConfiguration> configs) { if (configs == null) { // null means "no changes" return; diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index 85544d0a1b02..a6a607e4ce66 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -718,7 +718,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements GnssSatelliteBlacklistHelper gnssSatelliteBlacklistHelper = new GnssSatelliteBlacklistHelper(mContext, mLooper, this); - mHandler.post(gnssSatelliteBlacklistHelper::updateSatelliteBlacklist); mGnssBatchingProvider = new GnssBatchingProvider(); mGnssGeofenceProvider = new GnssGeofenceProvider(); @@ -746,6 +745,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements setAllowed(true); sendMessage(INITIALIZE_HANDLER, 0, null); + mHandler.post(gnssSatelliteBlacklistHelper::updateSatelliteBlacklist); } /** diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 87f0fb14ee33..d6557f6410ec 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -3910,11 +3910,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private void updateRulesForAppIdleParoleUL() { final boolean paroled = mAppStandby.isInParole(); final boolean enableChain = !paroled; - enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, enableChain); int ruleCount = mUidFirewallStandbyRules.size(); + final SparseIntArray blockedUids = new SparseIntArray(); for (int i = 0; i < ruleCount; i++) { final int uid = mUidFirewallStandbyRules.keyAt(i); + if (!isUidValidForBlacklistRulesUL(uid)) { + continue; + } int oldRules = mUidRules.get(uid); if (enableChain) { // Chain wasn't enabled before and the other power-related @@ -3926,13 +3929,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Skip if it had no restrictions to begin with if ((oldRules & MASK_ALL_NETWORKS) == 0) continue; } - final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules, paroled); + final boolean isUidIdle = !paroled && isUidIdle(uid); + if (isUidIdle && !mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid)) + && !isUidForegroundOnRestrictPowerUL(uid)) { + mUidFirewallStandbyRules.put(uid, FIREWALL_RULE_DENY); + blockedUids.put(uid, FIREWALL_RULE_DENY); + } else { + mUidFirewallStandbyRules.put(uid, FIREWALL_RULE_DEFAULT); + } + final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules, + isUidIdle); if (newUidRules == RULE_NONE) { mUidRules.delete(uid); } else { mUidRules.put(uid, newUidRules); } } + setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, blockedUids, + enableChain ? CHAIN_TOGGLE_ENABLE : CHAIN_TOGGLE_DISABLE); } /** @@ -4400,7 +4414,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private void updateRulesForPowerRestrictionsUL(int uid) { final int oldUidRules = mUidRules.get(uid, RULE_NONE); - final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false); + final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, + isUidIdle(uid)); if (newUidRules == RULE_NONE) { mUidRules.delete(uid); @@ -4414,33 +4429,33 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * * @param uid the uid of the app to update rules for * @param oldUidRules the current rules for the uid, in order to determine if there's a change - * @param paroled whether to ignore idle state of apps and only look at other restrictions + * @param isUidIdle whether uid is idle or not * * @return the new computed rules for the uid */ @GuardedBy("mUidRulesFirstLock") - private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) { + private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean isUidIdle) { if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/" - + (paroled ? "P" : "-")); + + (isUidIdle ? "I" : "-")); } try { - return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, paroled); + return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, isUidIdle); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } @GuardedBy("mUidRulesFirstLock") - private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, boolean paroled) { + private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, + boolean isUidIdle) { if (!isUidValidForBlacklistRulesUL(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid); return RULE_NONE; } - final boolean isIdle = !paroled && isUidIdle(uid); - final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode; + final boolean restrictMode = isUidIdle || mRestrictPower || mDeviceIdleMode; final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode); @@ -4463,7 +4478,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (LOGV) { Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")" - + ", isIdle: " + isIdle + + ", isIdle: " + isUidIdle + ", mRestrictPower: " + mRestrictPower + ", mDeviceIdleMode: " + mDeviceIdleMode + ", isForeground=" + isForeground @@ -5273,6 +5288,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void onTempPowerSaveWhitelistChange(int appId, boolean added) { synchronized (mUidRulesFirstLock) { + if (!mSystemReady) { + return; + } mLogger.tempPowerSaveWlChanged(appId, added); if (added) { mPowerSaveTempWhitelistAppIds.put(appId, true); diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java index a0ab5eae7315..cb1c7e4fd0de 100644 --- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java +++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java @@ -16,11 +16,13 @@ package com.android.server.net; +import static android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA; import static android.net.NetworkTemplate.getCollapsedRatType; import android.annotation.NonNull; import android.content.Context; import android.telephony.Annotation; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; @@ -195,7 +197,18 @@ public class NetworkStatsSubscriptionsMonitor extends @Override public void onServiceStateChanged(@NonNull ServiceState ss) { - final int networkType = ss.getDataNetworkType(); + // In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony + // would report RAT = 5G_NR. + // However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and + // network allocates a secondary 5G cell so telephony reports RAT = LTE along with + // NR state as connected. In such case, attributes the data usage to NR. + // See b/160727498. + final boolean is5GNsa = (ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE + || ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA) + && ss.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED; + + final int networkType = + (is5GNsa ? NETWORK_TYPE_5G_NSA : ss.getDataNetworkType()); final int collapsedRatType = getCollapsedRatType(networkType); if (collapsedRatType == mLastCollapsedRatType) return; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a2d0eaedbd44..b4ab57fa4dc8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -25539,7 +25539,8 @@ public class PackageManagerService extends IPackageManager.Stub private void applyMimeGroupChanges(String packageName, String mimeGroup) { if (mComponentResolver.updateMimeGroup(packageName, mimeGroup)) { - clearPackagePreferredActivities(packageName, UserHandle.USER_ALL); + Binder.withCleanCallingIdentity(() -> + clearPackagePreferredActivities(packageName, UserHandle.USER_ALL)); } mPmInternal.writeSettings(false); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 936ba7f3b097..3de2dc2fdef3 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -1407,7 +1407,7 @@ public final class Settings { if (pa.mPref.getParseError() == null) { final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId); ArrayList<PreferredActivity> pal = resolver.findFilters(pa); - if (pal == null || pal.size() == 0) { + if (pal == null || pal.size() == 0 || pa.mPref.mAlways) { resolver.addFilter(pa); } } else { diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 8868a6c2e6d8..b9431a6968f9 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -141,6 +141,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { @IntDef({NAV_BAR_LEFT, NAV_BAR_RIGHT, NAV_BAR_BOTTOM}) @interface NavigationBarPosition {} + @Retention(SOURCE) + @IntDef({ALT_BAR_UNKNOWN, ALT_BAR_LEFT, ALT_BAR_RIGHT, ALT_BAR_BOTTOM, ALT_BAR_TOP}) + @interface AltBarPosition {} + /** * Pass this event to the user / app. To be returned from * {@link #interceptKeyBeforeQueueing}. @@ -396,14 +400,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { return false; } - /** - * Returns true if the window has a letterbox and any part of that letterbox overlaps with - * the given {@code rect}. - */ - default boolean isLetterboxedOverlappingWith(Rect rect) { - return false; - } - /** @return the current windowing mode of this window. */ int getWindowingMode(); diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index dbdef2368c7c..8979d8c6ba10 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -1145,6 +1145,15 @@ public class StatsPullAtomService extends SystemService { private void addDataUsageBytesTransferAtoms(@NonNull NetworkStatsExt statsExt, @NonNull List<StatsEvent> pulledData) { + + // Workaround for 5G NSA mode, see {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}. + // 5G NSA mode means the primary cell is LTE with a secondary connection to an + // NR cell. To mitigate risk, NetworkStats is currently storing this state as + // a fake RAT type rather than storing the boolean separately. + final boolean is5GNsa = statsExt.ratType == NetworkTemplate.NETWORK_TYPE_5G_NSA; + // Report NR connected in 5G non-standalone mode, or if the RAT type is NR to begin with. + final boolean isNR = is5GNsa || statsExt.ratType == TelephonyManager.NETWORK_TYPE_NR; + final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling for (int i = 0; i < statsExt.stats.size(); i++) { statsExt.stats.getValues(i, entry); @@ -1156,7 +1165,7 @@ public class StatsPullAtomService extends SystemService { .writeLong(entry.rxPackets) .writeLong(entry.txBytes) .writeLong(entry.txPackets) - .writeInt(statsExt.ratType) + .writeInt(is5GNsa ? TelephonyManager.NETWORK_TYPE_LTE : statsExt.ratType) // Fill information about subscription, these cannot be null since invalid data // would be filtered when adding into subInfo list. .writeString(statsExt.subInfo.mcc) @@ -1165,6 +1174,7 @@ public class StatsPullAtomService extends SystemService { .writeInt(statsExt.subInfo.isOpportunistic ? DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC : DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC) + .writeBoolean(isNR) .build(); pulledData.add(e); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 40e558c09965..3e9377ed0664 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1395,6 +1395,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** + * @see Letterbox#notIntersectsOrFullyContains(Rect) + */ + boolean letterboxNotIntersectsOrFullyContains(Rect rect) { + return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect); + } + + /** * @return {@code true} if there is a letterbox and any part of that letterbox overlaps with * the given {@code rect}. */ diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java index 123fb6c9d8e3..c1447553ba31 100644 --- a/services/core/java/com/android/server/wm/BarController.java +++ b/services/core/java/com/android/server/wm/BarController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static com.android.server.wm.BarControllerProto.STATE; import static com.android.server.wm.BarControllerProto.TRANSIENT_STATE; +import android.annotation.NonNull; import android.app.StatusBarManager; import android.graphics.Rect; import android.os.Handler; @@ -169,13 +170,23 @@ public class BarController { return vis; } + private Rect getContentFrame(@NonNull WindowState win) { + final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType); + return rotatedContentFrame != null ? rotatedContentFrame : mContentFrame; + } + + boolean isLightAppearanceAllowed(WindowState win) { + if (win == null) { + return true; + } + return !win.isLetterboxedOverlappingWith(getContentFrame(win)); + } + boolean isTransparentAllowed(WindowState win) { if (win == null) { return true; } - final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType); - final Rect contentFrame = rotatedContentFrame != null ? rotatedContentFrame : mContentFrame; - return !win.isLetterboxedOverlappingWith(contentFrame); + return win.letterboxNotIntersectsOrFullyContains(getContentFrame(win)); } boolean setBarShowingLw(final boolean show) { diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 5ae6f80b262d..6a90f2994238 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -102,6 +102,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED; +import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM; +import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT; +import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT; +import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP; +import static android.view.WindowManagerPolicyConstants.ALT_BAR_UNKNOWN; import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE; import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; @@ -314,6 +319,17 @@ public class DisplayPolicy { private int[] mNavigationBarHeightForRotationInCarMode = new int[4]; private int[] mNavigationBarWidthForRotationInCarMode = new int[4]; + // Alternative status bar for when flexible insets mapping is used to place the status bar on + // another side of the screen. + private WindowState mStatusBarAlt = null; + @WindowManagerPolicy.AltBarPosition + private int mStatusBarAltPosition = ALT_BAR_UNKNOWN; + // Alternative navigation bar for when flexible insets mapping is used to place the navigation + // bar elsewhere on the screen. + private WindowState mNavigationBarAlt = null; + @WindowManagerPolicy.AltBarPosition + private int mNavigationBarAltPosition = ALT_BAR_UNKNOWN; + /** See {@link #getNavigationBarFrameHeight} */ private int[] mNavigationBarFrameHeightForRotationDefault = new int[4]; @@ -431,7 +447,7 @@ public class DisplayPolicy { case MSG_REQUEST_TRANSIENT_BARS: synchronized (mLock) { WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS) - ? mStatusBar : mNavigationBar; + ? getStatusBar() : getNavigationBar(); if (targetBar != null) { requestTransientBars(targetBar); } @@ -494,6 +510,7 @@ public class DisplayPolicy { if (mStatusBar != null) { requestTransientBars(mStatusBar); } + checkAltBarSwipeForTransientBars(ALT_BAR_TOP); } } @@ -504,6 +521,7 @@ public class DisplayPolicy { && mNavigationBarPosition == NAV_BAR_BOTTOM) { requestTransientBars(mNavigationBar); } + checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM); } } @@ -520,6 +538,7 @@ public class DisplayPolicy { excludedRegion)) { requestTransientBars(mNavigationBar); } + checkAltBarSwipeForTransientBars(ALT_BAR_RIGHT); } excludedRegion.recycle(); } @@ -537,6 +556,7 @@ public class DisplayPolicy { excludedRegion)) { requestTransientBars(mNavigationBar); } + checkAltBarSwipeForTransientBars(ALT_BAR_LEFT); } excludedRegion.recycle(); } @@ -638,6 +658,15 @@ public class DisplayPolicy { mHandler.post(mGestureNavigationSettingsObserver::register); } + private void checkAltBarSwipeForTransientBars(@WindowManagerPolicy.AltBarPosition int pos) { + if (mStatusBarAlt != null && mStatusBarAltPosition == pos) { + requestTransientBars(mStatusBarAlt); + } + if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) { + requestTransientBars(mNavigationBarAlt); + } + } + void systemReady() { mSystemGestures.systemReady(); if (mService.mPointerLocationEnabled) { @@ -899,6 +928,14 @@ public class DisplayPolicy { } break; } + + // Check if alternate bars positions were updated. + if (mStatusBarAlt == win) { + mStatusBarAltPosition = getAltBarPosition(attrs); + } + if (mNavigationBarAlt == win) { + mNavigationBarAltPosition = getAltBarPosition(attrs); + } } /** @@ -938,10 +975,9 @@ public class DisplayPolicy { mContext.enforcePermission( android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, "DisplayPolicy"); - if (mStatusBar != null) { - if (mStatusBar.isAlive()) { - return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; - } + if ((mStatusBar != null && mStatusBar.isAlive()) + || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; } break; case TYPE_NOTIFICATION_SHADE: @@ -958,10 +994,9 @@ public class DisplayPolicy { mContext.enforcePermission( android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, "DisplayPolicy"); - if (mNavigationBar != null) { - if (mNavigationBar.isAlive()) { - return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; - } + if ((mNavigationBar != null && mNavigationBar.isAlive()) + || (mNavigationBarAlt != null && mNavigationBarAlt.isAlive())) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; } break; case TYPE_NAVIGATION_BAR_PANEL: @@ -993,6 +1028,23 @@ public class DisplayPolicy { android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, "DisplayPolicy"); enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes); + + for (@InternalInsetsType int insetType : attrs.providesInsetsTypes) { + switch (insetType) { + case ITYPE_STATUS_BAR: + if ((mStatusBar != null && mStatusBar.isAlive()) + || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; + } + break; + case ITYPE_NAVIGATION_BAR: + if ((mNavigationBar != null && mNavigationBar.isAlive()) + || (mNavigationBarAlt != null && mNavigationBarAlt.isAlive())) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; + } + break; + } + } } return ADD_OKAY; } @@ -1084,7 +1136,19 @@ public class DisplayPolicy { break; default: if (attrs.providesInsetsTypes != null) { - for (int insetsType : attrs.providesInsetsTypes) { + for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) { + switch (insetsType) { + case ITYPE_STATUS_BAR: + mStatusBarAlt = win; + mStatusBarController.setWindow(mStatusBarAlt); + mStatusBarAltPosition = getAltBarPosition(attrs); + break; + case ITYPE_NAVIGATION_BAR: + mNavigationBarAlt = win; + mNavigationBarController.setWindow(mNavigationBarAlt); + mNavigationBarAltPosition = getAltBarPosition(attrs); + break; + } mDisplayContent.setInsetProvider(insetsType, win, null); } } @@ -1092,6 +1156,22 @@ public class DisplayPolicy { } } + @WindowManagerPolicy.AltBarPosition + private int getAltBarPosition(WindowManager.LayoutParams params) { + switch (params.gravity) { + case Gravity.LEFT: + return ALT_BAR_LEFT; + case Gravity.RIGHT: + return ALT_BAR_RIGHT; + case Gravity.BOTTOM: + return ALT_BAR_BOTTOM; + case Gravity.TOP: + return ALT_BAR_TOP; + default: + return ALT_BAR_UNKNOWN; + } + } + TriConsumer<DisplayFrames, WindowState, Rect> getImeSourceFrameProvider() { return (displayFrames, windowState, inOutFrame) -> { if (mNavigationBar != null && navigationBarPosition(displayFrames.mDisplayWidth, @@ -1132,12 +1212,14 @@ public class DisplayPolicy { * @param win The window being removed. */ void removeWindowLw(WindowState win) { - if (mStatusBar == win) { + if (mStatusBar == win || mStatusBarAlt == win) { mStatusBar = null; + mStatusBarAlt = null; mStatusBarController.setWindow(null); mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null); - } else if (mNavigationBar == win) { + } else if (mNavigationBar == win || mNavigationBarAlt == win) { mNavigationBar = null; + mNavigationBarAlt = null; mNavigationBarController.setWindow(null); mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null); } else if (mNotificationShade == win) { @@ -1163,7 +1245,7 @@ public class DisplayPolicy { } WindowState getStatusBar() { - return mStatusBar; + return mStatusBar != null ? mStatusBar : mStatusBarAlt; } WindowState getNotificationShade() { @@ -1171,7 +1253,7 @@ public class DisplayPolicy { } WindowState getNavigationBar() { - return mNavigationBar; + return mNavigationBar != null ? mNavigationBar : mNavigationBarAlt; } /** @@ -1233,6 +1315,46 @@ public class DisplayPolicy { return R.anim.dock_left_enter; } } + } else if (win == mStatusBarAlt || win == mNavigationBarAlt) { + if (win.getAttrs().windowAnimations != 0) { + return ANIMATION_STYLEABLE; + } + + int pos = (win == mStatusBarAlt) ? mStatusBarAltPosition : mNavigationBarAltPosition; + + boolean isExitOrHide = transit == TRANSIT_EXIT || transit == TRANSIT_HIDE; + boolean isEnterOrShow = transit == TRANSIT_ENTER || transit == TRANSIT_SHOW; + + switch (pos) { + case ALT_BAR_LEFT: + if (isExitOrHide) { + return R.anim.dock_left_exit; + } else if (isEnterOrShow) { + return R.anim.dock_left_enter; + } + break; + case ALT_BAR_RIGHT: + if (isExitOrHide) { + return R.anim.dock_right_exit; + } else if (isEnterOrShow) { + return R.anim.dock_right_enter; + } + break; + case ALT_BAR_BOTTOM: + if (isExitOrHide) { + return R.anim.dock_bottom_exit; + } else if (isEnterOrShow) { + return R.anim.dock_bottom_enter; + } + break; + case ALT_BAR_TOP: + if (isExitOrHide) { + return R.anim.dock_top_exit; + } else if (isEnterOrShow) { + return R.anim.dock_top_enter; + } + break; + } } if (transit == TRANSIT_PREVIEW_DONE) { @@ -1591,7 +1713,7 @@ public class DisplayPolicy { mInputConsumer = null; Slog.v(TAG, INPUT_CONSUMER_NAVIGATION + " dismissed."); } - } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) { + } else if (mInputConsumer == null && getStatusBar() != null && canHideNavigationBar()) { mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer( mHandler.getLooper(), INPUT_CONSUMER_NAVIGATION, @@ -2677,10 +2799,10 @@ public class DisplayPolicy { mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded(); } - if (mStatusBar != null) { + if (getStatusBar() != null) { if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar + " top=" + mTopFullscreenOpaqueWindowState); - final boolean forceShowStatusBar = (mStatusBar.getAttrs().privateFlags + final boolean forceShowStatusBar = (getStatusBar().getAttrs().privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0; final boolean notificationShadeForcesShowingNavigation = mNotificationShade != null @@ -3166,6 +3288,16 @@ public class DisplayPolicy { return mNavigationBarPosition; } + @WindowManagerPolicy.AltBarPosition + int getAlternateStatusBarPosition() { + return mStatusBarAltPosition; + } + + @WindowManagerPolicy.AltBarPosition + int getAlternateNavBarPosition() { + return mNavigationBarAltPosition; + } + /** * A new window has been focused. */ @@ -3275,7 +3407,7 @@ public class DisplayPolicy { // keys, we let it keep controlling the visibility. final boolean lastFocusCanReceiveKeys = (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys()); - winCandidate = isKeyguardShowing() ? mNotificationShade + winCandidate = isKeyguardShowing() && !isKeyguardOccluded() ? mNotificationShade : lastFocusCanReceiveKeys ? mLastFocusedWindow : mTopFullscreenOpaqueWindowState; if (winCandidate == null) { @@ -3327,8 +3459,8 @@ public class DisplayPolicy { final boolean isFullscreen = (visibility & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) != 0 || (PolicyControl.getWindowFlags(win, win.mAttrs) & FLAG_FULLSCREEN) != 0 - || (mStatusBar != null && insetsPolicy.isHidden(ITYPE_STATUS_BAR)) - || (mNavigationBar != null && insetsPolicy.isHidden( + || (getStatusBar() != null && insetsPolicy.isHidden(ITYPE_STATUS_BAR)) + || (getNavigationBar() != null && insetsPolicy.isHidden( ITYPE_NAVIGATION_BAR)); final int behavior = win.mAttrs.insetsFlags.behavior; final boolean isImmersive = (visibility & (View.SYSTEM_UI_FLAG_IMMERSIVE @@ -3420,17 +3552,22 @@ public class DisplayPolicy { WindowState opaqueOrDimming) { final boolean onKeyguard = isKeyguardShowing() && !isKeyguardOccluded(); final WindowState statusColorWin = onKeyguard ? mNotificationShade : opaqueOrDimming; - if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) { - // If the top fullscreen-or-dimming window is also the top fullscreen, respect - // its light flag. - appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; - final int legacyAppearance = InsetsFlags.getAppearance( - PolicyControl.getSystemUiVisibility(statusColorWin, null)); - appearance |= (statusColorWin.mAttrs.insetsFlags.appearance | legacyAppearance) - & APPEARANCE_LIGHT_STATUS_BARS; - } else if (statusColorWin != null && statusColorWin.isDimming()) { - // Otherwise if it's dimming, clear the light flag. - appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; + if (statusColorWin != null) { + if (statusColorWin == opaque || onKeyguard) { + // If the top fullscreen-or-dimming window is also the top fullscreen, respect + // its light flag. + appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; + final int legacyAppearance = InsetsFlags.getAppearance( + PolicyControl.getSystemUiVisibility(statusColorWin, null)); + appearance |= (statusColorWin.mAttrs.insetsFlags.appearance | legacyAppearance) + & APPEARANCE_LIGHT_STATUS_BARS; + } else if (statusColorWin.isDimming()) { + // Otherwise if it's dimming, clear the light flag. + appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; + } + if (!mStatusBarController.isLightAppearanceAllowed(statusColorWin)) { + appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; + } } return appearance; } @@ -3495,8 +3632,7 @@ public class DisplayPolicy { return vis; } - @VisibleForTesting - static int updateLightNavigationBarAppearanceLw(int appearance, WindowState opaque, + private int updateLightNavigationBarAppearanceLw(int appearance, WindowState opaque, WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) { if (navColorWin != null) { @@ -3509,6 +3645,9 @@ public class DisplayPolicy { // Clear the light flag for dimming window. appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; } + if (!mNavigationBarController.isLightAppearanceAllowed(navColorWin)) { + appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; + } } return appearance; } @@ -3583,7 +3722,7 @@ public class DisplayPolicy { final boolean hideNavBarSysui = (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; - final boolean transientStatusBarAllowed = mStatusBar != null + final boolean transientStatusBarAllowed = getStatusBar() != null && (notificationShadeHasFocus || (!mForceShowSystemBars && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky)))); @@ -3741,7 +3880,7 @@ public class DisplayPolicy { // TODO(b/118118435): Remove this after migration private boolean isImmersiveMode(int vis) { final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - return mNavigationBar != null + return getNavigationBar() != null && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 && (vis & flags) != 0 && canHideNavigationBar(); @@ -3749,7 +3888,7 @@ public class DisplayPolicy { private boolean isImmersiveMode(WindowState win) { final int behavior = win.mAttrs.insetsFlags.behavior; - return mNavigationBar != null + return getNavigationBar() != null && canHideNavigationBar() && (behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) @@ -3830,8 +3969,8 @@ public class DisplayPolicy { public void takeScreenshot(int screenshotType, int source) { if (mScreenshotHelper != null) { mScreenshotHelper.takeScreenshot(screenshotType, - mStatusBar != null && mStatusBar.isVisibleLw(), - mNavigationBar != null && mNavigationBar.isVisibleLw(), + getStatusBar() != null && getStatusBar().isVisibleLw(), + getNavigationBar() != null && getNavigationBar().isVisibleLw(), source, mHandler, null /* completionConsumer */); } } @@ -3869,6 +4008,11 @@ public class DisplayPolicy { if (mStatusBar != null) { pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar); } + if (mStatusBarAlt != null) { + pw.print(prefix); pw.print("mStatusBarAlt="); pw.print(mStatusBarAlt); + pw.print(prefix); pw.print("mStatusBarAltPosition="); + pw.println(mStatusBarAltPosition); + } if (mNotificationShade != null) { pw.print(prefix); pw.print("mExpandedPanel="); pw.print(mNotificationShade); } @@ -3880,6 +4024,11 @@ public class DisplayPolicy { pw.print(prefix); pw.print("mNavigationBarPosition="); pw.println(mNavigationBarPosition); } + if (mNavigationBarAlt != null) { + pw.print(prefix); pw.print("mNavigationBarAlt="); pw.println(mNavigationBarAlt); + pw.print(prefix); pw.print("mNavigationBarAltPosition="); + pw.println(mNavigationBarAltPosition); + } if (mFocusedWindow != null) { pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow); } diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index a685886da032..28dcbcdf3cc7 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -77,10 +77,10 @@ public class Letterbox { mOuter.set(outer); mInner.set(inner); - mTop.layout(outer.left, outer.top, inner.right, inner.top, surfaceOrigin); - mLeft.layout(outer.left, inner.top, inner.left, outer.bottom, surfaceOrigin); - mBottom.layout(inner.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin); - mRight.layout(inner.right, outer.top, outer.right, inner.bottom, surfaceOrigin); + mTop.layout(outer.left, outer.top, outer.right, inner.top, surfaceOrigin); + mLeft.layout(outer.left, outer.top, inner.left, outer.bottom, surfaceOrigin); + mBottom.layout(outer.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin); + mRight.layout(inner.right, outer.top, outer.right, outer.bottom, surfaceOrigin); } @@ -101,6 +101,31 @@ public class Letterbox { } /** + * Returns {@code true} if the letterbox does not overlap with the bar, or the letterbox can + * fully cover the window frame. + * + * @param rect The area of the window frame. + */ + boolean notIntersectsOrFullyContains(Rect rect) { + int emptyCount = 0; + int noOverlappingCount = 0; + for (LetterboxSurface surface : mSurfaces) { + final Rect surfaceRect = surface.mLayoutFrameGlobal; + if (surfaceRect.isEmpty()) { + // empty letterbox + emptyCount++; + } else if (!Rect.intersects(surfaceRect, rect)) { + // no overlapping + noOverlappingCount++; + } else if (surfaceRect.contains(rect)) { + // overlapping and covered + return true; + } + } + return (emptyCount + noOverlappingCount) == mSurfaces.length; + } + + /** * Returns true if any part of the letterbox overlaps with the given {@code rect}. */ public boolean isOverlappingWith(Rect rect) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 4700864c03bc..84a389349204 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2300,8 +2300,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - boolean resumedOnDisplay = false; final DisplayContent display = getChildAt(displayNdx); + if (display.shouldSleep()) { + continue; + } + + boolean resumedOnDisplay = false; for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { @@ -2390,7 +2394,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // process the keyguard going away, which can happen before the sleep // token is released. As a result, it is important we resume the // activity here. - resumeFocusedStacksTopActivities(); + stack.resumeTopActivityUncheckedLocked(null, null); } // The visibility update must not be called before resuming the top, so the // display orientation can be updated first if needed. Otherwise there may diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index c2021303c11c..e85049cad2f4 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -615,8 +615,9 @@ class TaskSnapshotController { static Rect getSystemBarInsets(Rect frame, InsetsState state) { return state.calculateInsets(frame, null /* ignoringVisibilityState */, false /* isScreenRound */, false /* alwaysConsumeSystemBars */, - null /* displayCutout */, 0 /* legacySoftInputMode */, 0 /* legacySystemUiFlags */, - null /* typeSideMap */).getInsets(WindowInsets.Type.systemBars()).toRect(); + null /* displayCutout */, 0 /* legacySoftInputMode */, 0 /* legacyWindowFlags */, + 0 /* legacySystemUiFlags */, null /* typeSideMap */).getInsets( + WindowInsets.Type.systemBars()).toRect(); } void dump(PrintWriter pw, String prefix) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 371d6b56f687..b7ac54f3470e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2163,6 +2163,10 @@ public class WindowManagerService extends IWindowManager.Stub throw new IllegalArgumentException( "Window type can not be changed after the window is added."); } + if (!Arrays.equals(win.mAttrs.providesInsetsTypes, attrs.providesInsetsTypes)) { + throw new IllegalArgumentException( + "Insets types can not be changed after the window is added."); + } // Odd choice but less odd than embedding in copyFrom() if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY) diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 26bcf3b285ec..f0c2a74958d6 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.mInputMethodInputTarget == this) { + dc.setInputMethodInputTarget(null); + } if (dc.mInputMethodControlTarget == this) { dc.updateImeControlTarget(); } @@ -3797,7 +3800,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mActivityRecord.getBounds().equals(mTmpRect); } - @Override + /** + * @see Letterbox#notIntersectsOrFullyContains(Rect) + */ + boolean letterboxNotIntersectsOrFullyContains(Rect rect) { + return mActivityRecord == null + || mActivityRecord.letterboxNotIntersectsOrFullyContains(rect); + } + public boolean isLetterboxedOverlappingWith(Rect rect) { return mActivityRecord != null && mActivityRecord.isLetterboxOverlappingWith(rect); } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 508d2d477067..da45300ed318 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1592,12 +1592,6 @@ class WindowStateAnimator { if (mSurfaceController != null) { mSurfaceController.detachChildren(); } - // If the children are detached, it means the app is exiting. We don't want to tear the - // content down too early, otherwise we could end up with a flicker. By preserving the - // current surface, we ensure the content remains on screen until the window is completely - // removed. It also ensures that the old surface is cleaned up when started again since it - // forces mSurfaceController to be set to null. - preserveSurfaceLocked(); } void setOffsetPositionForStackResize(boolean offsetPositionForStackResize) { diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 3450c3ae9fb3..a5f0d045948c 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -1422,6 +1422,11 @@ static long elapsedMcs(Duration start, Duration end) { } // Extract lib files from zip, create new files in incfs and write data to them +// Lib files should be placed next to the APK file in the following matter: +// Example: +// /path/to/base.apk +// /path/to/lib/arm/first.so +// /path/to/lib/arm/second.so bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath, std::string_view libDirRelativePath, std::string_view abi, bool extractNativeLibs) { @@ -1433,9 +1438,13 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ return false; } + const auto targetLibPathRelativeToStorage = + path::join(path::dirname(normalizePathToStorage(*ifs, storage, apkFullPath)), + libDirRelativePath); + // First prepare target directories if they don't exist yet - if (auto res = makeDirs(*ifs, storage, libDirRelativePath, 0755)) { - LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath + if (auto res = makeDirs(*ifs, storage, targetLibPathRelativeToStorage, 0755)) { + LOG(ERROR) << "Failed to prepare target lib directory " << targetLibPathRelativeToStorage << " errno: " << res; return false; } @@ -1486,7 +1495,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ auto startFileTs = Clock::now(); const auto libName = path::basename(fileName); - auto targetLibPath = path::join(libDirRelativePath, libName); + auto targetLibPath = path::join(targetLibPathRelativeToStorage, libName); const auto targetLibPathAbsolute = normalizePathToStorage(*ifs, storage, targetLibPath); // If the extract file already exists, skip if (access(targetLibPathAbsolute.c_str(), F_OK) == 0) { diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 1a04d2ff8c29..4dec7a1a0ab9 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -218,6 +218,10 @@ public class AppStandbyControllerTests { } @Override + void updatePowerWhitelistCache() { + } + + @Override boolean isRestrictedBucketEnabled() { return mIsRestrictedBucketEnabled; } 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 b4f2a6c9c6c3..85736ab11e65 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -887,6 +887,21 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved() throws Exception { + final DisplayContent dc = createNewDisplay(); + + WindowState app = createWindow(null, TYPE_BASE_APPLICATION, dc, "app"); + + dc.mInputMethodInputTarget = app; + assertEquals(app, dc.computeImeControlTarget()); + + app.removeImmediately(); + + assertNull(dc.mInputMethodInputTarget); + assertNull(dc.computeImeControlTarget()); + } + + @Test public void testComputeImeControlTarget() throws Exception { final DisplayContent dc = createNewDisplay(); dc.setRemoteInsetsController(createDisplayWindowInsetsController()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 278de56dfea0..2f3afbcb6d72 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -40,8 +40,13 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DEC import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; +import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM; +import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT; +import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT; +import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; @@ -60,6 +65,7 @@ import android.util.Pair; import android.util.SparseArray; import android.view.DisplayCutout; import android.view.DisplayInfo; +import android.view.Gravity; import android.view.InsetsState; import android.view.WindowInsets.Side; import android.view.WindowInsets.Type; @@ -147,6 +153,8 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void addingWindow_withInsetsTypes() { + mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one. + WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES}; win.getFrameLw().set(0, 0, 500, 100); @@ -196,6 +204,47 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test + public void addingWindow_variousGravities_alternateBarPosUpdated() { + mDisplayPolicy.removeWindowLw(mNavBarWindow); // Removes the existing one. + + WindowState win1 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel1"); + win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; + win1.mAttrs.gravity = Gravity.TOP; + win1.getFrameLw().set(0, 0, 200, 500); + addWindow(win1); + + assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_TOP); + mDisplayPolicy.removeWindowLw(win1); + + WindowState win2 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel2"); + win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; + win2.mAttrs.gravity = Gravity.BOTTOM; + win2.getFrameLw().set(0, 0, 200, 500); + addWindow(win2); + + assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_BOTTOM); + mDisplayPolicy.removeWindowLw(win2); + + WindowState win3 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel3"); + win3.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; + win3.mAttrs.gravity = Gravity.LEFT; + win3.getFrameLw().set(0, 0, 200, 500); + addWindow(win3); + + assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_LEFT); + mDisplayPolicy.removeWindowLw(win3); + + WindowState win4 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel4"); + win4.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; + win4.mAttrs.gravity = Gravity.RIGHT; + win4.getFrameLw().set(0, 0, 200, 500); + addWindow(win4); + + assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_RIGHT); + mDisplayPolicy.removeWindowLw(win4); + } + + @Test public void layoutWindowLw_fitStatusBars() { mWindow.mAttrs.setFitInsetsTypes(Type.statusBars()); addWindow(mWindow); diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java index bf84aecdb6a0..2f3004bf6832 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java @@ -16,8 +16,8 @@ package com.android.server.wm; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; @@ -60,6 +60,103 @@ public class LetterboxTest { assertTrue(mLetterbox.isOverlappingWith(new Rect(0, 0, 1, 1))); } + private static final int TOP_BAR = 0x1; + private static final int BOTTOM_BAR = 0x2; + private static final int LEFT_BAR = 0x4; + private static final int RIGHT_BAR = 0x8; + @Test + public void testNotIntersectsOrFullyContains_usesGlobalCoordinates() { + final Rect outer = new Rect(0, 0, 10, 50); + final Point surfaceOrig = new Point(1000, 2000); + + final Rect topBar = new Rect(0, 0, 10, 2); + final Rect bottomBar = new Rect(0, 45, 10, 50); + final Rect leftBar = new Rect(0, 0, 2, 50); + final Rect rightBar = new Rect(8, 0, 10, 50); + + final LetterboxLayoutVerifier verifier = + new LetterboxLayoutVerifier(outer, surfaceOrig, mLetterbox); + verifier.setBarRect(topBar, bottomBar, leftBar, rightBar); + + // top + verifier.setInner(0, 2, 10, 50).verifyPositions(TOP_BAR | BOTTOM_BAR, BOTTOM_BAR); + // bottom + verifier.setInner(0, 0, 10, 45).verifyPositions(TOP_BAR | BOTTOM_BAR, TOP_BAR); + // left + verifier.setInner(2, 0, 10, 50).verifyPositions(LEFT_BAR | RIGHT_BAR, RIGHT_BAR); + // right + verifier.setInner(0, 0, 8, 50).verifyPositions(LEFT_BAR | RIGHT_BAR, LEFT_BAR); + // top + bottom + verifier.setInner(0, 2, 10, 45).verifyPositions(TOP_BAR | BOTTOM_BAR, 0); + // left + right + verifier.setInner(2, 0, 8, 50).verifyPositions(LEFT_BAR | RIGHT_BAR, 0); + // top + left + verifier.setInner(2, 2, 10, 50).verifyPositions(TOP_BAR | LEFT_BAR, 0); + // top + left + right + verifier.setInner(2, 2, 8, 50).verifyPositions(TOP_BAR | LEFT_BAR | RIGHT_BAR, 0); + // left + right + bottom + verifier.setInner(2, 0, 8, 45).verifyPositions(LEFT_BAR | RIGHT_BAR | BOTTOM_BAR, 0); + // all + verifier.setInner(2, 2, 8, 45) + .verifyPositions(TOP_BAR | BOTTOM_BAR | LEFT_BAR | RIGHT_BAR, 0); + } + + private static class LetterboxLayoutVerifier { + final Rect mOuter; + final Rect mInner = new Rect(); + final Point mSurfaceOrig; + final Letterbox mLetterbox; + final Rect mTempRect = new Rect(); + + final Rect mTop = new Rect(); + final Rect mBottom = new Rect(); + final Rect mLeft = new Rect(); + final Rect mRight = new Rect(); + + LetterboxLayoutVerifier(Rect outer, Point surfaceOrig, Letterbox letterbox) { + mOuter = new Rect(outer); + mSurfaceOrig = new Point(surfaceOrig); + mLetterbox = letterbox; + } + + LetterboxLayoutVerifier setInner(int left, int top, int right, int bottom) { + mInner.set(left, top, right, bottom); + mLetterbox.layout(mOuter, mInner, mSurfaceOrig); + return this; + } + + void setBarRect(Rect top, Rect bottom, Rect left, Rect right) { + mTop.set(top); + mBottom.set(bottom); + mLeft.set(left); + mRight.set(right); + } + + void verifyPositions(int allowedPos, int noOverlapPos) { + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTop), + (allowedPos & TOP_BAR) != 0); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mBottom), + (allowedPos & BOTTOM_BAR) != 0); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mLeft), + (allowedPos & LEFT_BAR) != 0); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mRight), + (allowedPos & RIGHT_BAR) != 0); + + mTempRect.set(mTop.left, mTop.top, mTop.right, mTop.bottom + 1); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & TOP_BAR) != 0); + mTempRect.set(mLeft.left, mLeft.top, mLeft.right + 1, mLeft.bottom); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & LEFT_BAR) != 0); + mTempRect.set(mRight.left - 1, mRight.top, mRight.right, mRight.bottom); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & RIGHT_BAR) != 0); + mTempRect.set(mBottom.left, mBottom.top - 1, mBottom.right, mBottom.bottom); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & BOTTOM_BAR) != 0); + } + } + @Test public void testSurfaceOrigin_applied() { mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000)); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 51db099676b0..c848736a64c2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -896,6 +896,24 @@ public class RootActivityContainerTests extends ActivityTestsBase { assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask()); } + @Test + public void testResumeFocusedStackOnSleepingDisplay() { + // Create an activity on secondary display. + final TestDisplayContent secondDisplay = addNewDisplayContentAt( + DisplayContent.POSITION_TOP); + final ActivityStack stack = secondDisplay.getDefaultTaskDisplayArea() + .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mService).setStack(stack).build(); + spyOn(activity); + spyOn(stack); + + // Cannot resumed activities on secondary display if the display should sleep. + doReturn(true).when(secondDisplay).shouldSleep(); + mRootWindowContainer.resumeFocusedStacksTopActivities(); + verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any()); + verify(activity, never()).makeActiveIfNeeded(any()); + } + /** * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity * info for test cases. diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java index 31a102ae3bad..ef74861e9422 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java @@ -25,8 +25,8 @@ import static android.view.Gravity.BOTTOM; import static android.view.Gravity.LEFT; import static android.view.Gravity.RIGHT; import static android.view.Gravity.TOP; -import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; -import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; @@ -190,7 +190,7 @@ public class ScreenDecorWindowTests { @Test public void testProvidesInsetsTypes() { - int[] providesInsetsTypes = new int[]{ITYPE_STATUS_BAR}; + int[] providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR}; final View win = createWindow("StatusBarSubPanel", TOP, MATCH_PARENT, mDecorThickness, RED, FLAG_LAYOUT_IN_SCREEN, 0, providesInsetsTypes); @@ -199,7 +199,7 @@ public class ScreenDecorWindowTests { private View createDecorWindow(int gravity, int width, int height) { int[] providesInsetsTypes = - new int[]{gravity == TOP ? ITYPE_STATUS_BAR : ITYPE_NAVIGATION_BAR}; + new int[]{gravity == TOP ? ITYPE_CLIMATE_BAR : ITYPE_EXTRA_NAVIGATION_BAR}; return createWindow("decorWindow", gravity, width, height, RED, FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR, providesInsetsTypes); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 318b15293d8d..52f1e37e69f4 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2345,6 +2345,16 @@ public class CarrierConfigManager { "call_forwarding_blocks_while_roaming_string_array"; /** + * Call forwarding number prefixes defined by {@link + * #KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY} which will be allowed while the + * device is reporting that it is roaming and IMS is registered over LTE or Wi-Fi. + * By default this value is {@code true}. + * @hide + */ + public static final String KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL = + "support_ims_call_forwarding_while_roaming_bool"; + + /** * The day of the month (1-31) on which the data cycle rolls over. * <p> * If the current month does not have this day, the cycle will roll over at @@ -4174,6 +4184,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false); sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY, null); + sDefaults.putBoolean(KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL, true); sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0); sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null); sDefaults.putBoolean(KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false); diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt index 5dd0fda4da28..9ba56e44fe88 100644 --- a/tests/net/java/android/net/NetworkTemplateTest.kt +++ b/tests/net/java/android/net/NetworkTemplateTest.kt @@ -26,6 +26,7 @@ import android.net.NetworkStats.METERED_ALL import android.net.NetworkStats.ROAMING_ALL import android.net.NetworkTemplate.MATCH_MOBILE import android.net.NetworkTemplate.MATCH_WIFI +import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA import android.net.NetworkTemplate.NETWORK_TYPE_ALL import android.net.NetworkTemplate.buildTemplateMobileWithRatType import android.telephony.TelephonyManager @@ -145,11 +146,13 @@ class NetworkTemplateTest { assertParcelSane(templateWifi, 8) } - // Verify NETWORK_TYPE_ALL does not conflict with TelephonyManager#NETWORK_TYPE_* constants. + // Verify NETWORK_TYPE_* constants in NetworkTemplate do not conflict with + // TelephonyManager#NETWORK_TYPE_* constants. @Test - fun testNetworkTypeAll() { + fun testNetworkTypeConstants() { for (ratType in TelephonyManager.getAllNetworkTypes()) { assertNotEquals(NETWORK_TYPE_ALL, ratType) + assertNotEquals(NETWORK_TYPE_5G_NSA, ratType) } } } diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java index 058856dcd6fb..c91dfecf041b 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java @@ -17,6 +17,7 @@ package com.android.server.net; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; @@ -29,7 +30,9 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.content.Context; +import android.net.NetworkTemplate; import android.os.Looper; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; @@ -61,7 +64,6 @@ public final class NetworkStatsSubscriptionsMonitorTest { private static final String TEST_IMSI3 = "466929999999999"; @Mock private Context mContext; - @Mock private PhoneStateListener mPhoneStateListener; @Mock private SubscriptionManager mSubscriptionManager; @Mock private TelephonyManager mTelephonyManager; @Mock private Delegate mDelegate; @@ -215,4 +217,55 @@ public final class NetworkStatsSubscriptionsMonitorTest { verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE)); assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); } + + + @Test + public void test5g() { + mMonitor.start(); + // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback + // before changing RAT type. Also capture listener for later use. + addTestSub(TEST_SUBID1, TEST_IMSI1); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor = + ArgumentCaptor.forClass(RatTypeListener.class); + verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); + final RatTypeListener listener = CollectionUtils + .find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == TEST_SUBID1); + assertNotNull(listener); + + // Set RAT type to 5G NSA (non-standalone) mode, verify the monitor outputs + // NETWORK_TYPE_5G_NSA. + final ServiceState serviceState = mock(ServiceState.class); + when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE); + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED); + listener.onServiceStateChanged(serviceState); + assertRatTypeChangedForSub(TEST_IMSI1, NetworkTemplate.NETWORK_TYPE_5G_NSA); + reset(mDelegate); + + // Set RAT type to LTE without NR connected, the RAT type should be downgraded to LTE. + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE); + listener.onServiceStateChanged(serviceState); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE); + reset(mDelegate); + + // Verify NR connected with other RAT type does not take effect. + when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_UMTS); + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED); + listener.onServiceStateChanged(serviceState); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); + reset(mDelegate); + + // Set RAT type to 5G standalone mode, the RAT type should be NR. + setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_NR); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR); + reset(mDelegate); + + // Set NR state to none in standalone mode does not change anything. + when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_NR); + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE); + listener.onServiceStateChanged(serviceState); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR); + } } |