Merge "Merge SP1A.211105.004 to aosp-master - DO NOT MERGE"

This commit is contained in:
Bill Yi 2021-11-03 17:50:39 +00:00 committed by Gerrit Code Review
commit 982bb0f145
32 changed files with 452 additions and 140 deletions

View File

@ -31,6 +31,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.util.Objects;
import java.util.Set;
/**
@ -86,6 +87,12 @@ public class Account implements Parcelable {
if (TextUtils.isEmpty(type)) {
throw new IllegalArgumentException("the type must not be empty: " + type);
}
if (name.length() > 200) {
throw new IllegalArgumentException("account name is longer than 200 characters");
}
if (type.length() > 200) {
throw new IllegalArgumentException("account type is longer than 200 characters");
}
this.name = name;
this.type = type;
this.accessId = accessId;

View File

@ -464,11 +464,7 @@ public final class ActivityThread extends ClientTransactionHandler
@Override
public int hashCode() {
return hashCode(authority, userId);
}
public static int hashCode(final String auth, final int userIdent) {
return ((auth != null) ? auth.hashCode() : 0) ^ userIdent;
return ((authority != null) ? authority.hashCode() : 0) ^ userId;
}
}
@ -490,7 +486,7 @@ public final class ActivityThread extends ClientTransactionHandler
// Note we never removes items from this map but that's okay because there are only so many
// users and so many authorities.
@GuardedBy("mGetProviderKeys")
final SparseArray<ProviderKey> mGetProviderKeys = new SparseArray<>();
final ArrayMap<ProviderKey, ProviderKey> mGetProviderKeys = new ArrayMap<>();
final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
= new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
@ -7020,11 +7016,11 @@ public final class ActivityThread extends ClientTransactionHandler
}
private ProviderKey getGetProviderKey(String auth, int userId) {
final int key = ProviderKey.hashCode(auth, userId);
final ProviderKey key = new ProviderKey(auth, userId);
synchronized (mGetProviderKeys) {
ProviderKey lock = mGetProviderKeys.get(key);
if (lock == null) {
lock = new ProviderKey(auth, userId);
lock = key;
mGetProviderKeys.put(key, lock);
}
return lock;

View File

@ -1340,7 +1340,10 @@ public final class BluetoothDevice implements Parcelable, Attributable {
if (alias == null) {
return getName();
}
return alias;
return alias
.replace('\t', ' ')
.replace('\n', ' ')
.replace('\r', ' ');
} catch (RemoteException e) {
Log.e(TAG, "", e);
}

View File

@ -102,6 +102,13 @@ public abstract class PlayerBase {
mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE;
};
/** @hide */
public int getPlayerIId() {
synchronized (mLock) {
return mPlayerIId;
}
}
/**
* Call from derived class when instantiation / initialization is successful
*/

View File

@ -93,9 +93,9 @@ public class CompanionDeviceActivity extends Activity {
final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
setTitle(Html.fromHtml(getString(
R.string.confirmation_title,
getCallingAppName(),
profileName,
selectedDevice.getDisplayName()), 0));
Html.escapeHtml(getCallingAppName()),
Html.escapeHtml(selectedDevice.getDisplayName())), 0));
mPairButton = findViewById(R.id.button_pair);
mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice));
getService().mSelectedDevice = selectedDevice;
@ -108,8 +108,8 @@ public class CompanionDeviceActivity extends Activity {
mPairButton = findViewById(R.id.button_pair);
mPairButton.setVisibility(View.GONE);
setTitle(Html.fromHtml(getString(R.string.chooser_title,
profileName,
getCallingAppName()), 0));
Html.escapeHtml(profileName),
Html.escapeHtml(getCallingAppName())), 0));
mDeviceListView = findViewById(R.id.device_list);
mDevicesAdapter = new DevicesAdapter();
mDeviceListView.setAdapter(mDevicesAdapter);

View File

@ -139,7 +139,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
+ " with ducking", e);
}
player.start();
if (DEBUG) { Log.d(mTag, "player.start"); }
if (DEBUG) { Log.d(mTag, "player.start piid:" + player.getPlayerIId()); }
} catch (Exception e) {
if (player != null) {
player.release();
@ -155,7 +155,13 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
mPlayer = player;
}
if (mp != null) {
if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
if (DEBUG) {
Log.d(mTag, "mPlayer.pause+release piid:" + player.getPlayerIId());
}
mp.pause();
try {
Thread.sleep(100);
} catch (InterruptedException ie) { }
mp.release();
}
this.notify();
@ -244,6 +250,10 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
try {
mp.stop();
} catch (Exception e) { }
if (DEBUG) {
Log.i(mTag, "About to release MediaPlayer piid:"
+ mp.getPlayerIId() + " due to notif cancelled");
}
mp.release();
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
@ -284,7 +294,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
public void onCompletion(MediaPlayer mp) {
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
if (DEBUG) Log.d(mTag, "onCompletion() abandonning AudioFocus");
if (DEBUG) Log.d(mTag, "onCompletion() abandoning AudioFocus");
mAudioManagerWithAudioFocus.abandonAudioFocus(null);
mAudioManagerWithAudioFocus = null;
} else {
@ -310,6 +320,10 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
}
}
if (mp != null) {
if (DEBUG) {
Log.i("NotificationPlayer", "About to release MediaPlayer piid:"
+ mp.getPlayerIId() + " due to onCompletion");
}
mp.release();
}
}

View File

@ -51,6 +51,7 @@ import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@ -95,6 +96,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private final UiEventLogger mUiEventLogger;
private final InstanceIdSequence mInstanceIdSequence;
private final CustomTileStatePersister mCustomTileStatePersister;
private final FeatureFlags mFeatureFlags;
private final List<Callback> mCallbacks = new ArrayList<>();
private AutoTileManager mAutoTiles;
@ -122,7 +124,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
UiEventLogger uiEventLogger,
UserTracker userTracker,
SecureSettings secureSettings,
CustomTileStatePersister customTileStatePersister
CustomTileStatePersister customTileStatePersister,
FeatureFlags featureFlags
) {
mIconController = iconController;
mContext = context;
@ -144,6 +147,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mUserTracker = userTracker;
mSecureSettings = secureSettings;
mCustomTileStatePersister = customTileStatePersister;
mFeatureFlags = featureFlags;
mainHandler.post(() -> {
// This is technically a hack to avoid circular dependency of
@ -265,7 +269,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
}
final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
final List<String> tileSpecs = loadTileSpecs(mContext, newValue, mFeatureFlags);
int currentUser = mUserTracker.getUserId();
if (currentUser != mCurrentUser) {
mUserContext = mUserTracker.getUserContext();
@ -334,7 +338,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
// If we didn't manage to create any tiles, set it to empty (default)
Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
changeTiles(currentSpecs, loadTileSpecs(mContext, "", mFeatureFlags));
} else {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onTilesChanged();
@ -389,7 +393,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
final List<String> tileSpecs = loadTileSpecs(mContext, setting);
final List<String> tileSpecs = loadTileSpecs(mContext, setting, mFeatureFlags);
if (changeFunction.test(tileSpecs)) {
saveTilesToSettings(tileSpecs);
}
@ -478,7 +482,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
}
protected static List<String> loadTileSpecs(Context context, String tileList) {
protected static List<String> loadTileSpecs(
Context context, String tileList, FeatureFlags featureFlags) {
final Resources res = context.getResources();
if (TextUtils.isEmpty(tileList)) {
@ -511,6 +516,21 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
}
}
}
if (featureFlags.isProviderModelSettingEnabled()) {
if (!tiles.contains("internet")) {
if (tiles.contains("wifi")) {
// Replace the WiFi with Internet, and remove the Cell
tiles.set(tiles.indexOf("wifi"), "internet");
tiles.remove("cell");
} else if (tiles.contains("cell")) {
// Replace the Cell with Internet
tiles.set(tiles.indexOf("cell"), "internet");
}
} else {
tiles.remove("wifi");
tiles.remove("cell");
}
}
return tiles;
}

View File

@ -41,6 +41,7 @@ import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.util.leak.GarbageMonitor;
import java.util.ArrayList;
@ -62,6 +63,7 @@ public class TileQueryHelper {
private final Executor mBgExecutor;
private final Context mContext;
private final UserTracker mUserTracker;
private final FeatureFlags mFeatureFlags;
private TileStateListener mListener;
private boolean mFinished;
@ -71,12 +73,14 @@ public class TileQueryHelper {
Context context,
UserTracker userTracker,
@Main Executor mainExecutor,
@Background Executor bgExecutor
@Background Executor bgExecutor,
FeatureFlags featureFlags
) {
mContext = context;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
mUserTracker = userTracker;
mFeatureFlags = featureFlags;
}
public void setListener(TileStateListener listener) {
@ -117,6 +121,10 @@ public class TileQueryHelper {
}
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
if (mFeatureFlags.isProviderModelSettingEnabled()) {
possibleTiles.remove("cell");
possibleTiles.remove("wifi");
}
for (String spec : possibleTiles) {
// Only add current and stock tiles that can be created from QSFactoryImpl.

View File

@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.row;
import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@ -28,15 +27,12 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
public class FooterView extends StackScrollerDecorView {
private final int mClearAllTopPadding;
private FooterViewButton mDismissButton;
private FooterViewButton mManageButton;
private boolean mShowHistory;
public FooterView(Context context, AttributeSet attrs) {
super(context, attrs);
mClearAllTopPadding = context.getResources().getDimensionPixelSize(
R.dimen.clear_all_padding_top);
}
@Override
@ -55,11 +51,6 @@ public class FooterView extends StackScrollerDecorView {
mManageButton = findViewById(R.id.manage_text);
}
public void setTextColor(@ColorInt int color) {
mManageButton.setTextColor(color);
mDismissButton.setTextColor(color);
}
public void setManageButtonClickListener(OnClickListener listener) {
mManageButton.setOnClickListener(listener);
}
@ -95,21 +86,25 @@ public class FooterView extends StackScrollerDecorView {
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int textColor = getResources().getColor(R.color.notif_pill_text);
Resources.Theme theme = getContext().getTheme();
mDismissButton.setBackground(
getResources().getDrawable(R.drawable.notif_footer_btn_background, theme));
mDismissButton.setTextColor(textColor);
mManageButton.setBackground(
getResources().getDrawable(R.drawable.notif_footer_btn_background, theme));
mManageButton = findViewById(R.id.manage_text);
updateColors();
mDismissButton.setText(R.string.clear_all_notifications_text);
mManageButton.setTextColor(textColor);
mDismissButton.setContentDescription(
mContext.getString(R.string.accessibility_clear_all));
showHistory(mShowHistory);
}
/**
* Update the text and background colors for the current color palette and night mode setting.
*/
public void updateColors() {
Resources.Theme theme = mContext.getTheme();
int textColor = getResources().getColor(R.color.notif_pill_text, theme);
mDismissButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
mDismissButton.setTextColor(textColor);
mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
mManageButton.setTextColor(textColor);
}
@Override
public ExpandableViewState createExpandableViewState() {
return new FooterViewState();

View File

@ -4231,7 +4231,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final @ColorInt int textColor =
Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
mSectionsManager.setHeaderForegroundColor(textColor);
mFooterView.setTextColor(textColor);
mFooterView.updateColors();
mEmptyShadeView.setTextColor(textColor);
}

View File

@ -139,7 +139,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
() -> mock(AutoTileManager.class), mock(DumpManager.class),
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
mock(SecureSettings.class), mock(CustomTileStatePersister.class));
mock(SecureSettings.class), mock(CustomTileStatePersister.class), mFeatureFlags);
qs.setHost(host);
qs.setListening(true);

View File

@ -63,6 +63,7 @@ import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@ -124,6 +125,8 @@ public class QSTileHostTest extends SysuiTestCase {
private SecureSettings mSecureSettings;
@Mock
private CustomTileStatePersister mCustomTileStatePersister;
@Mock
private FeatureFlags mFeatureFlags;
private Handler mHandler;
private TestableLooper mLooper;
@ -137,9 +140,9 @@ public class QSTileHostTest extends SysuiTestCase {
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
mSecureSettings, mCustomTileStatePersister);
mSecureSettings, mCustomTileStatePersister, mFeatureFlags);
setUpTileFactory();
when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false);
when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
.thenReturn("");
}
@ -169,13 +172,13 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testLoadTileSpecs_emptySetting() {
List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
List<String> tiles = QSTileHost.loadTileSpecs(mContext, "", mFeatureFlags);
assertFalse(tiles.isEmpty());
}
@Test
public void testLoadTileSpecs_nullSetting() {
List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
List<String> tiles = QSTileHost.loadTileSpecs(mContext, null, mFeatureFlags);
assertFalse(tiles.isEmpty());
}
@ -188,6 +191,55 @@ public class QSTileHostTest extends SysuiTestCase {
assertEquals(2, mQSTileHost.getTiles().size());
}
@Test
public void testRemoveWifiAndCellularWithoutInternet() {
when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2");
assertEquals("internet", mQSTileHost.mTileSpecs.get(0));
assertEquals("spec1", mQSTileHost.mTileSpecs.get(1));
assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
}
@Test
public void testRemoveWifiAndCellularWithInternet() {
when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2, internet");
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
assertEquals("internet", mQSTileHost.mTileSpecs.get(2));
}
@Test
public void testRemoveWifiWithoutInternet() {
when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, wifi, spec2");
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
assertEquals("internet", mQSTileHost.mTileSpecs.get(1));
assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
}
@Test
public void testRemoveCellWithInternet() {
when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, spec2, cell, internet");
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
assertEquals("internet", mQSTileHost.mTileSpecs.get(2));
}
@Test
public void testNoWifiNoCellularNoInternet() {
when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
}
@Test
public void testSpecWithInvalidDoesNotUseDefault() {
mContext.getOrCreateTestableResources()
@ -321,7 +373,7 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testLoadTileSpec_repeated() {
List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");
List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2", mFeatureFlags);
assertEquals(2, specs.size());
assertEquals("spec1", specs.get(0));
@ -332,7 +384,7 @@ public class QSTileHostTest extends SysuiTestCase {
public void testLoadTileSpec_repeatedInDefault() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");
List<String> specs = QSTileHost.loadTileSpecs(mContext, "default", mFeatureFlags);
// Remove spurious tiles, like dbg:mem
specs.removeIf(spec -> !"spec1".equals(spec));
@ -343,7 +395,7 @@ public class QSTileHostTest extends SysuiTestCase {
public void testLoadTileSpec_repeatedDefaultAndSetting() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1");
List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");
List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1", mFeatureFlags);
// Remove spurious tiles, like dbg:mem
specs.removeIf(spec -> !"spec1".equals(spec));
@ -371,11 +423,12 @@ public class QSTileHostTest extends SysuiTestCase {
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
UiEventLogger uiEventLogger, UserTracker userTracker,
SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister) {
SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
FeatureFlags featureFlags) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
customTileStatePersister);
customTileStatePersister, featureFlags);
}
@Override

View File

@ -58,6 +58,7 @@ import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@ -108,6 +109,8 @@ public class TileQueryHelperTest extends SysuiTestCase {
private PackageManager mPackageManager;
@Mock
private UserTracker mUserTracker;
@Mock
private FeatureFlags mFeatureFlags;
@Captor
private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor;
@ -133,12 +136,12 @@ public class TileQueryHelperTest extends SysuiTestCase {
}
}
).when(mQSTileHost).createTile(anyString());
when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false);
FakeSystemClock clock = new FakeSystemClock();
mMainExecutor = new FakeExecutor(clock);
mBgExecutor = new FakeExecutor(clock);
mTileQueryHelper = new TileQueryHelper(
mContext, mUserTracker, mMainExecutor, mBgExecutor);
mContext, mUserTracker, mMainExecutor, mBgExecutor, mFeatureFlags);
mTileQueryHelper.setListener(mListener);
}

View File

@ -48,6 +48,7 @@ import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@ -98,6 +99,8 @@ public class TileServicesTest extends SysuiTestCase {
private UserTracker mUserTracker;
@Mock
private SecureSettings mSecureSettings;
@Mock
private FeatureFlags mFeatureFlags;
@Before
public void setUp() throws Exception {
@ -119,7 +122,8 @@ public class TileServicesTest extends SysuiTestCase {
mUiEventLogger,
mUserTracker,
mSecureSettings,
mock(CustomTileStatePersister.class));
mock(CustomTileStatePersister.class),
mFeatureFlags);
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
mUserTracker);
}

View File

@ -1834,6 +1834,11 @@ public class AccountManagerService
+ ", skipping since the account already exists");
return false;
}
if (accounts.accountsDb.findAllDeAccounts().size() > 100) {
Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
+ ", skipping since more than 50 accounts on device exist");
return false;
}
long accountId = accounts.accountsDb.insertCeAccount(account, password);
if (accountId < 0) {
Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()

View File

@ -2582,14 +2582,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
if (mCurToken != null) {
try {
if (DEBUG) {
Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
+ mCurTokenDisplayId);
}
mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId);
} catch (RemoteException e) {
if (DEBUG) {
Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
+ mCurTokenDisplayId);
}
mWindowManagerInternal.removeWindowToken(mCurToken, false /* removeWindows */,
false /* animateExit */, mCurTokenDisplayId);
// Set IME window status as invisible when unbind current method.
mImeWindowVis = 0;
mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;

View File

@ -215,7 +215,7 @@ public class DefaultCrossProfileIntentFiltersUtils {
private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH =
new DefaultCrossProfileIntentFilter.Builder(
DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
/* flags= */0,
/* flags= */ ONLY_IF_NO_MATCH_FOUND,
/* letsPersonalDataIntoProfile= */ false)
.addAction(ACTION_RECOGNIZE_SPEECH)
.addCategory(Intent.CATEGORY_DEFAULT)

View File

@ -480,9 +480,10 @@ public final class Permission {
r.append("DUP:");
r.append(permissionInfo.name);
}
if (permission.isRuntime() && (ownerChanged || wasNonRuntime)) {
// If this is a runtime permission and the owner has changed, or this wasn't a runtime
// permission, then permission state should be cleaned up
if ((permission.isInternal() && ownerChanged)
|| (permission.isRuntime() && (ownerChanged || wasNonRuntime))) {
// If this is an internal/runtime permission and the owner has changed, or this wasn't a
// runtime permission, then permission state should be cleaned up.
permission.mDefinitionChanged = true;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {

View File

@ -1643,7 +1643,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
isRolePermission = permission.isRole();
}
final boolean mayRevokeRolePermission = isRolePermission
&& mayManageRolePermission(callingUid);
// Allow ourselves to revoke role permissions due to definition changes.
&& (callingUid == Process.myUid() || mayManageRolePermission(callingUid));
final boolean isRuntimePermission;
synchronized (mLock) {
@ -2321,11 +2322,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int permNum = 0; permNum < numPermissions; permNum++) {
final String permName = permissionsToRevoke.get(permNum);
final boolean isInternalPermission;
synchronized (mLock) {
final Permission bp = mRegistry.getPermission(permName);
if (bp == null || !bp.isRuntime()) {
if (bp == null || !(bp.isInternal() || bp.isRuntime())) {
continue;
}
isInternalPermission = bp.isInternal();
}
mPackageManagerInt.forEachPackage(pkg -> {
final String packageName = pkg.getPackageName();
@ -2345,12 +2348,18 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (permissionState == PackageManager.PERMISSION_GRANTED
&& (flags & flagMask) == 0) {
final int uid = UserHandle.getUid(userId, appId);
EventLog.writeEvent(0x534e4554, "154505240", uid,
"Revoking permission " + permName + " from package "
+ packageName + " due to definition change");
EventLog.writeEvent(0x534e4554, "168319670", uid,
"Revoking permission " + permName + " from package "
+ packageName + " due to definition change");
if (isInternalPermission) {
EventLog.writeEvent(0x534e4554, "195338390", uid,
"Revoking permission " + permName + " from package "
+ packageName + " due to definition change");
} else {
EventLog.writeEvent(0x534e4554, "154505240", uid,
"Revoking permission " + permName + " from package "
+ packageName + " due to definition change");
EventLog.writeEvent(0x534e4554, "168319670", uid,
"Revoking permission " + permName + " from package "
+ packageName + " due to definition change");
}
Slog.e(TAG, "Revoking permission " + permName + " from package "
+ packageName + " due to definition change");
try {

View File

@ -2304,10 +2304,9 @@ public final class TvInputManagerService extends SystemService {
public void requestChannelBrowsable(Uri channelUri, int userId)
throws RemoteException {
final String callingPackageName = getCallingPackageName();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Binder.getCallingUid(), userId, "requestChannelBrowsable");
final long identity = Binder.clearCallingIdentity();
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "requestChannelBrowsable");
try {
Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED);
List<ResolveInfo> list = getContext().getPackageManager()

View File

@ -655,6 +655,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Whether the IME is showing when transitioning away from this activity. */
boolean mLastImeShown;
/**
* When set to true, the IME insets will be frozen until the next app becomes IME input target.
* @see InsetsPolicy#adjustVisibilityForIme
*/
boolean mImeInsetsFrozenUntilStartInput;
/**
* A flag to determine if this AR is in the process of closing or entering PIP. This is needed
* to help AR know that the app is in the process of closing but hasn't yet started closing on
@ -1357,6 +1363,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
if (newTask != null && isState(RESUMED)) {
newTask.setResumedActivity(this, "onParentChanged");
mImeInsetsFrozenUntilStartInput = false;
}
if (rootTask != null && rootTask.topRunningActivity() == this) {
@ -4767,6 +4774,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
&& imeInputTarget.getWindow().mActivityRecord == this
&& mDisplayContent.mInputMethodWindow != null
&& mDisplayContent.mInputMethodWindow.isVisible();
mImeInsetsFrozenUntilStartInput = true;
}
final DisplayContent displayContent = getDisplayContent();
@ -5885,6 +5893,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// closing activity having to wait until idle timeout to be stopped or destroyed if the
// next activity won't report idle (e.g. repeated view animation).
mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
// If the activity is visible, but no windows are eligible to start input, unfreeze
// to avoid permanently frozen IME insets.
if (mImeInsetsFrozenUntilStartInput && getWindow(
win -> WindowManager.LayoutParams.mayUseInputMethod(win.mAttrs.flags))
== null) {
mImeInsetsFrozenUntilStartInput = false;
}
}
}
@ -7800,6 +7816,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
@Override
void onResize() {
// Reset freezing IME insets flag when the activity resized.
mImeInsetsFrozenUntilStartInput = false;
super.onResize();
}
/** Returns true if the configuration is compatible with this activity. */
boolean isConfigurationCompatible(Configuration config) {
final int orientation = getRequestedOrientation();

View File

@ -1165,10 +1165,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
WindowToken removeWindowToken(IBinder binder) {
WindowToken removeWindowToken(IBinder binder, boolean animateExit) {
final WindowToken token = mTokenMap.remove(binder);
if (token != null && token.asActivityRecord() == null) {
token.setExiting();
token.setExiting(animateExit);
}
return token;
}
@ -1252,7 +1252,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
void removeAppToken(IBinder binder) {
final WindowToken token = removeWindowToken(binder);
final WindowToken token = removeWindowToken(binder, true /* animateExit */);
if (token == null) {
Slog.w(TAG_WM, "removeAppToken: Attempted to remove non-existing token: " + binder);
return;
@ -3971,6 +3971,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void updateImeInputAndControlTarget(WindowState target) {
if (mImeInputTarget != target) {
ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
if (target != null && target.mActivityRecord != null) {
target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
}
setImeInputTarget(target);
updateImeControlTarget();
}

View File

@ -211,7 +211,7 @@ class InsetsPolicy {
InsetsState getInsetsForWindow(WindowState target) {
final InsetsState originalState = mStateController.getInsetsForWindow(target);
final InsetsState state = adjustVisibilityForTransientTypes(originalState);
return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state;
return adjustVisibilityForIme(target, state, state == originalState);
}
/**
@ -241,16 +241,37 @@ class InsetsPolicy {
return state;
}
// Navigation bar insets is always visible to IME.
private static InsetsState adjustVisibilityForIme(InsetsState originalState,
private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
boolean copyState) {
final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
if (originalNavSource != null && !originalNavSource.isVisible()) {
final InsetsState state = copyState ? new InsetsState(originalState) : originalState;
final InsetsSource navSource = new InsetsSource(originalNavSource);
navSource.setVisible(true);
state.addSource(navSource);
return state;
if (w.mIsImWindow) {
// Navigation bar insets is always visible to IME.
final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
if (originalNavSource != null && !originalNavSource.isVisible()) {
final InsetsState state = copyState ? new InsetsState(originalState)
: originalState;
final InsetsSource navSource = new InsetsSource(originalNavSource);
navSource.setVisible(true);
state.addSource(navSource);
return state;
}
} else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) {
// During switching tasks with gestural navigation, if the IME is attached to
// one app window on that time, even the next app window is behind the IME window,
// conceptually the window should not receive the IME insets if the next window is
// not eligible IME requester and ready to show IME on top of it.
final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp();
final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME);
if (shouldImeAttachedToApp && originalImeSource != null) {
final boolean imeVisibility =
w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME);
final InsetsState state = copyState ? new InsetsState(originalState)
: originalState;
final InsetsSource imeSource = new InsetsSource(originalImeSource);
imeSource.setVisible(imeVisibility);
state.addSource(imeSource);
return state;
}
}
return originalState;
}

View File

@ -66,8 +66,8 @@ class WallpaperWindowToken extends WindowToken {
}
@Override
void setExiting() {
super.setExiting();
void setExiting(boolean animateExit) {
super.setExiting(animateExit);
mDisplayContent.mWallpaperController.removeWallpaperToken(this);
}

View File

@ -445,8 +445,21 @@ public abstract class WindowManagerInternal {
* @param removeWindows Whether to also remove the windows associated with the token.
* @param displayId The display to remove the token from.
*/
public final void removeWindowToken(android.os.IBinder token, boolean removeWindows,
int displayId) {
removeWindowToken(token, removeWindows, true /* animateExit */, displayId);
}
/**
* Removes a window token.
*
* @param token The toke to remove.
* @param removeWindows Whether to also remove the windows associated with the token.
* @param animateExit Whether to play the windows exit animation after the token removal.
* @param displayId The display to remove the token from.
*/
public abstract void removeWindowToken(android.os.IBinder token, boolean removeWindows,
int displayId);
boolean animateExit, int displayId);
/**
* Registers a listener to be notified about app transition events.

View File

@ -2816,6 +2816,31 @@ public class WindowManagerService extends IWindowManager.Stub
}
void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit,
int displayId) {
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
+ " for non-exiting displayId=%d", binder, displayId);
return;
}
final WindowToken token = dc.removeWindowToken(binder, animateExit);
if (token == null) {
ProtoLog.w(WM_ERROR,
"removeWindowToken: Attempted to remove non-existing token: %s",
binder);
return;
}
if (removeWindows) {
token.removeAllWindowsIfPossible();
}
dc.getInputMonitor().updateInputWindowsLw(true /* force */);
}
}
@Override
public void removeWindowToken(IBinder binder, int displayId) {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
@ -2823,23 +2848,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
+ " for non-exiting displayId=%d", binder, displayId);
return;
}
final WindowToken token = dc.removeWindowToken(binder);
if (token == null) {
ProtoLog.w(WM_ERROR,
"removeWindowToken: Attempted to remove non-existing token: %s",
binder);
return;
}
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
}
removeWindowToken(binder, false /* removeWindows */, true /* animateExit */, displayId);
} finally {
Binder.restoreCallingIdentity(origId);
}
@ -7536,28 +7545,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
public void removeWindowToken(IBinder binder, boolean removeWindows, int displayId) {
synchronized (mGlobalLock) {
if (removeWindows) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
+ " for non-exiting displayId=%d", binder, displayId);
return;
}
final WindowToken token = dc.removeWindowToken(binder);
if (token == null) {
ProtoLog.w(WM_ERROR,
"removeWindowToken: Attempted to remove non-existing token: %s",
binder);
return;
}
token.removeAllWindowsIfPossible();
}
WindowManagerService.this.removeWindowToken(binder, displayId);
}
public void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit,
int displayId) {
WindowManagerService.this.removeWindowToken(binder, removeWindows, animateExit,
displayId);
}
@Override

View File

@ -2180,11 +2180,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
boolean onSetAppExiting() {
boolean onSetAppExiting(boolean animateExit) {
final DisplayContent displayContent = getDisplayContent();
boolean changed = false;
if (isVisibleNow()) {
if (!animateExit) {
// Hide the window permanently if no window exist animation is performed, so we can
// avoid the window surface becoming visible again unexpectedly during the next
// relayout.
mPermanentlyHidden = true;
hide(false /* doAnimation */, false /* requestAnim */);
}
if (isVisibleNow() && animateExit) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
if (mWmService.mAccessibilityController != null) {
mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT);
@ -2197,7 +2204,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState c = mChildren.get(i);
changed |= c.onSetAppExiting();
changed |= c.onSetAppExiting(animateExit);
}
return changed;

View File

@ -24,6 +24,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@ -232,7 +233,7 @@ class WindowToken extends WindowContainer<WindowState> {
}
}
void setExiting() {
void setExiting(boolean animateExit) {
if (isEmpty()) {
super.removeImmediately();
return;
@ -247,11 +248,12 @@ class WindowToken extends WindowContainer<WindowState> {
final int count = mChildren.size();
boolean changed = false;
final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN);
final boolean delayed = isAnimating(TRANSITION | PARENTS)
|| (isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION) && animateExit);
for (int i = 0; i < count; i++) {
final WindowState win = mChildren.get(i);
changed |= win.onSetAppExiting();
changed |= win.onSetAppExiting(animateExit);
}
final ActivityRecord app = asActivityRecord();
@ -353,7 +355,7 @@ class WindowToken extends WindowContainer<WindowState> {
@Override
void removeImmediately() {
if (mDisplayContent != null) {
mDisplayContent.removeWindowToken(token);
mDisplayContent.removeWindowToken(token, true /* animateExit */);
}
// Needs to occur after the token is removed from the display above to avoid attempt at
// duplicate removal of this window container from it's parent.

View File

@ -39,6 +39,7 @@ import static android.os.Process.NOBODY_UID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@ -2816,6 +2817,73 @@ public class ActivityRecordTests extends WindowTestsBase {
assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
}
@Test
public void testImeInsetsFrozenFlag_resetWhenReparented() {
final ActivityRecord activity = createActivityWithTask();
final WindowState app = createWindow(null, TYPE_APPLICATION, activity, "app");
final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
final Task newTask = new TaskBuilder(mSupervisor).build();
makeWindowVisible(app, imeWindow);
mDisplayContent.mInputMethodWindow = imeWindow;
mDisplayContent.setImeLayeringTarget(app);
mDisplayContent.setImeInputTarget(app);
// Simulate app is closing and expect the last IME is shown and IME insets is frozen.
app.mActivityRecord.commitVisibility(false, false);
assertTrue(app.mActivityRecord.mLastImeShown);
assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
// Expect IME insets frozen state will reset when the activity is reparent to the new task.
activity.setState(RESUMED, "test");
activity.reparent(newTask, 0 /* top */, "test");
assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@Test
public void testImeInsetsFrozenFlag_resetWhenResized() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
makeWindowVisibleAndDrawn(app, mImeWindow);
mDisplayContent.setImeLayeringTarget(app);
mDisplayContent.setImeInputTarget(app);
// Simulate app is closing and expect the last IME is shown and IME insets is frozen.
app.mActivityRecord.commitVisibility(false, false);
assertTrue(app.mActivityRecord.mLastImeShown);
assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
// Expect IME insets frozen state will reset when the activity is reparent to the new task.
app.mActivityRecord.onResize();
assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@Test
public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
makeWindowVisibleAndDrawn(app, mImeWindow);
mDisplayContent.setImeLayeringTarget(app);
mDisplayContent.setImeInputTarget(app);
// Simulate app is closing and expect the last IME is shown and IME insets is frozen.
app.mActivityRecord.commitVisibility(false, false);
app.mActivityRecord.onWindowsGone();
assertTrue(app.mActivityRecord.mLastImeShown);
assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
// Expect IME insets frozen state will reset when the activity has no IME focusable window.
app.mActivityRecord.forAllWindowsUnchecked(w -> {
w.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
return true;
}, true);
app.mActivityRecord.commitVisibility(true, false);
app.mActivityRecord.onWindowsVisible();
assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
}
private void assertHasStartingWindow(ActivityRecord atoken) {
assertNotNull(atoken.mStartingSurface);
assertNotNull(atoken.mStartingData);

View File

@ -258,6 +258,7 @@ public class SystemServicesTestRule implements TestRule {
final ActivityManagerInternal amInternal = mAmService.mInternal;
spyOn(amInternal);
doNothing().when(amInternal).trimApplications();
doNothing().when(amInternal).scheduleAppGcs();
doNothing().when(amInternal).updateCpuStats();
doNothing().when(amInternal).updateOomAdj();
doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean());

View File

@ -891,6 +891,40 @@ public class WindowStateTests extends WindowTestsBase {
assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
}
@Test
public void testAdjustImeInsetsVisibilityWhenSwitchingApps() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
spyOn(imeWindow);
doReturn(true).when(imeWindow).isVisible();
mDisplayContent.mInputMethodWindow = imeWindow;
final InsetsStateController controller = mDisplayContent.getInsetsStateController();
controller.getImeSourceProvider().setWindow(imeWindow, null, null);
// Simulate app requests IME with updating all windows Insets State when IME is above app.
mDisplayContent.setImeLayeringTarget(app);
mDisplayContent.setImeInputTarget(app);
assertTrue(mDisplayContent.shouldImeAttachedToApp());
controller.getImeSourceProvider().scheduleShowImePostLayout(app);
controller.getImeSourceProvider().getSource().setVisible(true);
controller.updateAboveInsetsState(imeWindow, false);
// Expect all app windows behind IME can receive IME insets visible.
assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
assertTrue(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
// Simulate app plays closing transition to app2.
app.mActivityRecord.commitVisibility(false, false);
assertTrue(app.mActivityRecord.mLastImeShown);
assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
// Verify the IME insets is visible on app, but not for app2 during app task switching.
assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
}
@UseTestDisplay(addWindows = { W_ACTIVITY })
@Test
public void testUpdateImeControlTargetWhenLeavingMultiWindow() {

View File

@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@ -44,6 +45,7 @@ import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.util.function.BiFunction;
@ -126,7 +128,7 @@ public class WindowTokenTests extends WindowTestsBase {
final WindowState window1 = createWindow(null, TYPE_TOAST, token, "window1");
final WindowState window2 = createWindow(null, TYPE_TOAST, token, "window2");
mDisplayContent.removeWindowToken(token.token);
mDisplayContent.removeWindowToken(token.token, true /* animateExit */);
// Verify that the token is no longer mapped on the display
assertNull(mDisplayContent.getWindowToken(token.token));
// Verify that the token is still attached to its parent
@ -261,4 +263,29 @@ public class WindowTokenTests extends WindowTestsBase {
assertNotNull(app.getFrozenInsetsState());
assertNull(mDisplayContent.mInputMethodWindow.getFrozenInsetsState());
}
@Test
public void testRemoveWindowToken_noAnimateExitWhenSet() {
final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
final WindowState win = createWindow(null, TYPE_APPLICATION, token, "win");
makeWindowVisible(win);
assertTrue(win.isOnScreen());
spyOn(win);
spyOn(win.mWinAnimator);
spyOn(win.mToken);
// Invoking removeWindowToken with setting no window exit animation and not remove window
// immediately. verify the window will hide without applying exit animation.
mWm.removeWindowToken(win.mToken.token, false /* removeWindows */, false /* animateExit */,
mDisplayContent.mDisplayId);
verify(win).onSetAppExiting(Mockito.eq(false) /* animateExit */);
verify(win).hide(false /* doAnimation */, false /* requestAnim */);
assertFalse(win.isOnScreen());
verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false);
assertTrue(win.mToken.hasChild());
// Even though the window is being removed afterwards, it won't apply exit animation.
win.removeIfPossible();
verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false);
}
}