Merge "add certs to carrier configs and store them as UiccAccessRules"
diff --git a/Android.bp b/Android.bp
index b20b085..9d05ffd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -248,10 +248,6 @@
name: "framework-minus-apex",
defaults: ["framework-defaults"],
javac_shard_size: 150,
- required: [
- "framework-platform-compat-config",
- "libcore-platform-compat-config",
- ],
}
java_library {
@@ -262,6 +258,10 @@
"framework-minus-apex",
"jobscheduler-framework",
],
+ required: [
+ "framework-platform-compat-config",
+ "libcore-platform-compat-config",
+ ],
sdk_version: "core_platform",
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 4ea3726..65c11d7 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -17,6 +17,7 @@
package android.content;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_ERRORED;
@@ -645,9 +646,11 @@
}
boolean checkUser(int pid, int uid, Context context) {
- return UserHandle.getUserId(uid) == context.getUserId()
- || mSingleUser
- || context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
+ if (UserHandle.getUserId(uid) == context.getUserId() || mSingleUser) {
+ return true;
+ }
+ return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid) == PERMISSION_GRANTED
+ || context.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
== PERMISSION_GRANTED;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bb5ced5..2c53faa 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -38,6 +38,7 @@
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.VrManager;
+import android.compat.IPlatformCompat;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
@@ -3228,6 +3229,7 @@
ROLE_SERVICE,
//@hide ROLE_CONTROLLER_SERVICE,
CAMERA_SERVICE,
+ //@hide: PLATFORM_COMPAT_SERVICE,
PRINT_SERVICE,
CONSUMER_IR_SERVICE,
//@hide: TRUST_SERVICE,
@@ -4597,6 +4599,13 @@
public static final String STATS_MANAGER = "stats";
/**
+ * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+ * {@link IPlatformCompat} IBinder for communicating with the platform compat service.
+ * @hide
+ */
+ public static final String PLATFORM_COMPAT_SERVICE = "platform_compat";
+
+ /**
* Service to capture a bugreport.
* @see #getSystemService(String)
* @see android.os.BugreportManager
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 73b8a48..1609f53 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -74,7 +74,10 @@
}
/**
- * Returns a list of all currently available rollbacks.
+ * Returns a list of all currently available rollbacks. This includes ones for very recently
+ * installed packages (even if onFinished has not yet been called). As a result, packages that
+ * very recently failed to install may also be included, but those rollbacks will fail with
+ * 'rollback not available'.
*
* @throws SecurityException if the caller does not have appropriate permissions.
*/
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 0513fee..356b344 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.app.Dialog;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
@@ -50,6 +51,7 @@
final int mWindowType;
final int mGravity;
final boolean mTakesFocus;
+ final boolean mAutomotiveHideNavBarForKeyboard;
private final Rect mBounds = new Rect();
@Retention(SOURCE)
@@ -134,6 +136,8 @@
mWindowType = windowType;
mGravity = gravity;
mTakesFocus = takesFocus;
+ mAutomotiveHideNavBarForKeyboard = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
initDockWindow();
}
@@ -247,6 +251,11 @@
windowModFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
}
+ if (isAutomotive() && mAutomotiveHideNavBarForKeyboard) {
+ windowSetFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ windowModFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ }
+
getWindow().setFlags(windowSetFlags, windowModFlags);
}
@@ -338,6 +347,10 @@
mWindowState = newState;
}
+ private boolean isAutomotive() {
+ return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
private static String stateToString(@SoftInputWindowState int state) {
switch (state) {
case SoftInputWindowState.TOKEN_PENDING:
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4cff611..bfafa2f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16,16 +16,19 @@
package android.provider;
-import static android.provider.SettingsValidators.ANY_INTEGER_VALIDATOR;
-import static android.provider.SettingsValidators.ANY_STRING_VALIDATOR;
-import static android.provider.SettingsValidators.BOOLEAN_VALIDATOR;
-import static android.provider.SettingsValidators.COMPONENT_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
-import static android.provider.SettingsValidators.LOCALE_VALIDATOR;
-import static android.provider.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
-import static android.provider.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.PACKAGE_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.URI_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR;
import android.Manifest;
import android.annotation.IntDef;
@@ -80,7 +83,12 @@
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.provider.SettingsValidators.Validator;
+import android.provider.settings.validators.ComponentNameListValidator;
+import android.provider.settings.validators.DiscreteValueValidator;
+import android.provider.settings.validators.InclusiveFloatRangeValidator;
+import android.provider.settings.validators.InclusiveIntegerRangeValidator;
+import android.provider.settings.validators.PackageNameListValidator;
+import android.provider.settings.validators.Validator;
import android.speech.tts.TextToSpeech;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
@@ -3149,7 +3157,7 @@
public static final String END_BUTTON_BEHAVIOR = "end_button_behavior";
private static final Validator END_BUTTON_BEHAVIOR_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+ new InclusiveIntegerRangeValidator(0, 3);
/**
* END_BUTTON_BEHAVIOR value for "go home".
@@ -3351,7 +3359,7 @@
"bluetooth_discoverability";
private static final Validator BLUETOOTH_DISCOVERABILITY_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+ new InclusiveIntegerRangeValidator(0, 2);
/**
* Bluetooth discoverability timeout. If this value is nonzero, then
@@ -3495,7 +3503,7 @@
public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
private static final Validator PEAK_REFRESH_RATE_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(24f, Float.MAX_VALUE);
+ new InclusiveFloatRangeValidator(24f, Float.MAX_VALUE);
/**
* The amount of time in milliseconds before the device goes to sleep or begins
@@ -3524,7 +3532,7 @@
public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr";
private static final Validator SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 255);
+ new InclusiveIntegerRangeValidator(0, 255);
/**
* Control whether to enable automatic brightness mode.
@@ -3542,7 +3550,7 @@
public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj";
private static final Validator SCREEN_AUTO_BRIGHTNESS_ADJ_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(-1, 1);
+ new InclusiveFloatRangeValidator(-1, 1);
/**
* SCREEN_BRIGHTNESS_MODE value for manual mode.
@@ -3676,7 +3684,7 @@
"haptic_feedback_intensity";
private static final Validator VIBRATION_INTENSITY_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+ new InclusiveIntegerRangeValidator(0, 3);
/**
* Ringer volume. This is used internally, changing this value will not
@@ -3766,7 +3774,7 @@
public static final String MASTER_BALANCE = "master_balance";
private static final Validator MASTER_BALANCE_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(-1.f, 1.f);
+ new InclusiveFloatRangeValidator(-1.f, 1.f);
/**
* Whether the notifications should use the ring volume (value of 1) or
@@ -4004,7 +4012,7 @@
/** @hide */
public static final Validator TIME_12_24_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"12", "24", null});
+ new DiscreteValueValidator(new String[] {"12", "24", null});
/**
* Date format string
@@ -4090,7 +4098,7 @@
/** @hide */
public static final Validator USER_ROTATION_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+ new InclusiveIntegerRangeValidator(0, 3);
/**
* Control whether the rotation lock toggle in the System UI should be hidden.
@@ -4179,7 +4187,7 @@
/** @hide */
public static final Validator TTY_MODE_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+ new InclusiveIntegerRangeValidator(0, 3);
/**
* Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
@@ -4381,7 +4389,7 @@
/** @hide */
public static final Validator SIP_CALL_OPTIONS_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(
+ new DiscreteValueValidator(
new String[] {"SIP_ALWAYS", "SIP_ADDRESS_ONLY"});
/**
@@ -4428,7 +4436,7 @@
/** @hide */
public static final Validator POINTER_SPEED_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(-7, 7);
+ new InclusiveFloatRangeValidator(-7, 7);
/**
* Whether lock-to-app will be triggered by long-press on recents.
@@ -6352,7 +6360,7 @@
public static final String LOCK_SCREEN_CUSTOM_CLOCK_FACE = "lock_screen_custom_clock_face";
private static final Validator LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR =
- SettingsValidators.JSON_OBJECT_VALIDATOR;
+ JSON_OBJECT_VALIDATOR;
/**
* Indicates which clock face to show on lock screen and AOD while docked.
@@ -6509,7 +6517,7 @@
"enabled_accessibility_services";
private static final Validator ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(":");
+ new ComponentNameListValidator(":");
/**
* List of the accessibility services to which the user has granted
@@ -6521,7 +6529,7 @@
"touch_exploration_granted_accessibility_services";
private static final Validator TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(":");
+ new ComponentNameListValidator(":");
/**
* Whether the Global Actions Panel is enabled.
@@ -6696,7 +6704,7 @@
"accessibility_display_magnification_scale";
private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
+ new InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
/**
* Unused mangnification setting
@@ -6780,7 +6788,7 @@
"accessibility_captioning_preset";
private static final Validator ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
+ new DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
"3", "4"});
/**
@@ -6824,7 +6832,7 @@
"accessibility_captioning_edge_type";
private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2"});
+ new DiscreteValueValidator(new String[]{"0", "1", "2"});
/**
* Integer property that specifes the edge color for captions as a
@@ -6870,7 +6878,7 @@
"accessibility_captioning_typeface";
private static final Validator ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"DEFAULT",
+ new DiscreteValueValidator(new String[]{"DEFAULT",
"MONOSPACE", "SANS_SERIF", "SERIF"});
/**
@@ -6882,7 +6890,7 @@
"accessibility_captioning_font_scale";
private static final Validator ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(0.5f, 2.0f);
+ new InclusiveFloatRangeValidator(0.5f, 2.0f);
/**
* Setting that specifies whether display color inversion is enabled.
@@ -6923,7 +6931,7 @@
"accessibility_display_daltonizer";
private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(
+ new DiscreteValueValidator(
new String[] {"-1", "0", "11", "12", "13"});
/**
@@ -7112,8 +7120,7 @@
*/
public static final String TTS_DEFAULT_LOCALE = "tts_default_locale";
- private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR =
- new SettingsValidators.TTSListValidator();
+ private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = TTS_LIST_VALIDATOR;
/**
* Space delimited list of plugin packages that are enabled.
@@ -7121,7 +7128,7 @@
public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
private static final Validator TTS_ENABLED_PLUGINS_VALIDATOR =
- new SettingsValidators.PackageNameListValidator(" ");
+ new PackageNameListValidator(" ");
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON}
@@ -7315,7 +7322,7 @@
"preferred_tty_mode";
private static final Validator PREFERRED_TTY_MODE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
+ new DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
/**
* Whether the enhanced voice privacy mode is enabled.
@@ -7630,7 +7637,7 @@
public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior";
private static final Validator INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"1", "2"});
+ new DiscreteValueValidator(new String[]{"1", "2"});
/**
* INCALL_POWER_BUTTON_BEHAVIOR value for "turn off screen".
@@ -7851,7 +7858,7 @@
public static final String UI_NIGHT_MODE = "ui_night_mode";
private static final Validator UI_NIGHT_MODE_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+ new InclusiveIntegerRangeValidator(0, 2);
/**
* Whether screensavers are enabled.
@@ -7871,7 +7878,7 @@
public static final String SCREENSAVER_COMPONENTS = "screensaver_components";
private static final Validator SCREENSAVER_COMPONENTS_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(",");
+ new ComponentNameListValidator(",");
/**
* If screensavers are enabled, whether the screensaver should be automatically launched
@@ -8023,7 +8030,7 @@
"enabled_notification_assistant";
private static final Validator ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(":");
+ new ComponentNameListValidator(":");
/**
* Read only list of the service components that the current user has explicitly allowed to
@@ -8038,7 +8045,7 @@
public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
private static final Validator ENABLED_NOTIFICATION_LISTENERS_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(":");
+ new ComponentNameListValidator(":");
/**
* Read only list of the packages that the current user has explicitly allowed to
@@ -8053,7 +8060,7 @@
"enabled_notification_policy_access_packages";
private static final Validator ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR =
- new SettingsValidators.PackageNameListValidator(":");
+ new PackageNameListValidator(":");
/**
* Defines whether managed profile ringtones should be synced from it's parent profile
@@ -8350,16 +8357,6 @@
BOOLEAN_VALIDATOR;
/**
- * Whether or not the face unlock education screen has been shown to the user.
- * @hide
- */
- public static final String FACE_UNLOCK_EDUCATION_INFO_DISPLAYED =
- "face_unlock_education_info_displayed";
-
- private static final Validator FACE_UNLOCK_EDUCATION_INFO_DISPLAYED_VALIDATOR =
- BOOLEAN_VALIDATOR;
-
- /**
* Whether or not debugging is enabled.
* @hide
*/
@@ -8446,7 +8443,7 @@
public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+ new InclusiveIntegerRangeValidator(0, 2);
/**
* Control the color temperature of Night Display, represented in Kelvin.
@@ -8507,7 +8504,7 @@
public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
private static final Validator ENABLED_VR_LISTENERS_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(":");
+ new ComponentNameListValidator(":");
/**
* Behavior of the display while in VR mode.
@@ -8519,7 +8516,7 @@
public static final String VR_DISPLAY_MODE = "vr_display_mode";
private static final Validator VR_DISPLAY_MODE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1"});
+ new DiscreteValueValidator(new String[]{"0", "1"});
/**
* Lower the display persistence while the system is in VR mode.
@@ -8638,8 +8635,7 @@
*/
public static final String QS_TILES = "sysui_qs_tiles";
- private static final Validator QS_TILES_VALIDATOR =
- new SettingsValidators.TileListValidator();
+ private static final Validator QS_TILES_VALIDATOR = TILE_LIST_VALIDATOR;
/**
* Specifies whether the web action API is enabled.
@@ -8705,8 +8701,7 @@
*/
public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
- private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR =
- new SettingsValidators.TileListValidator();
+ private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = TILE_LIST_VALIDATOR;
/**
* Whether the Lockdown button should be shown in the power menu.
@@ -8867,7 +8862,7 @@
"theme_customization_overlay_packages";
private static final Validator THEME_CUSTOMIZATION_OVERLAY_PACKAGES_VALIDATOR =
- SettingsValidators.JSON_OBJECT_VALIDATOR;
+ JSON_OBJECT_VALIDATOR;
/**
* Navigation bar mode.
@@ -8879,7 +8874,7 @@
public static final String NAVIGATION_MODE =
"navigation_mode";
private static final Validator NAVIGATION_MODE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"});
+ new DiscreteValueValidator(new String[] {"0", "1", "2"});
/**
* Controls whether aware is enabled.
@@ -9182,8 +9177,6 @@
VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR);
VALIDATORS.put(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR);
- VALIDATORS.put(FACE_UNLOCK_EDUCATION_INFO_DISPLAYED,
- FACE_UNLOCK_EDUCATION_INFO_DISPLAYED_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
@@ -10733,7 +10726,7 @@
"network_recommendations_enabled";
private static final Validator NETWORK_RECOMMENDATIONS_ENABLED_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "1"});
+ new DiscreteValueValidator(new String[] {"-1", "0", "1"});
/**
* Which package name to use for network recommendations. If null, network recommendations
@@ -12584,7 +12577,7 @@
public static final String EMERGENCY_TONE = "emergency_tone";
private static final Validator EMERGENCY_TONE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"});
+ new DiscreteValueValidator(new String[] {"0", "1", "2"});
/**
* CDMA only settings
@@ -12614,7 +12607,7 @@
"enable_automatic_system_server_heap_dumps";
private static final Validator ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+ new DiscreteValueValidator(new String[] {"0", "1"});
/**
* See RIL_PreferredNetworkType in ril.h
@@ -12808,7 +12801,7 @@
"low_power_sticky_auto_disable_level";
private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+ new InclusiveIntegerRangeValidator(0, 100);
/**
* Whether sticky battery saver should be deactivated once the battery level has reached the
@@ -12820,7 +12813,7 @@
"low_power_sticky_auto_disable_enabled";
private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+ new DiscreteValueValidator(new String[] {"0", "1"});
/**
* Battery level [1-100] at which low power mode automatically turns on.
@@ -12835,7 +12828,7 @@
public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+ new InclusiveIntegerRangeValidator(0, 100);
/**
* Whether battery saver is currently set to trigger based on percentage, dynamic power
@@ -12848,7 +12841,7 @@
public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode";
private static final Validator AUTOMATIC_POWER_SAVE_MODE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+ new DiscreteValueValidator(new String[] {"0", "1"});
/**
* The setting that backs the disable threshold for the setPowerSavingsWarning api in
@@ -12861,7 +12854,7 @@
public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD =
"dynamic_power_savings_disable_threshold";
private static final Validator DYNAMIC_POWER_SAVINGS_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+ new InclusiveIntegerRangeValidator(0, 100);
/**
* The setting which backs the setDynamicPowerSaveHint api in PowerManager.
@@ -13011,7 +13004,7 @@
public static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output";
private static final Validator ENCODED_SURROUND_OUTPUT_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2", "3"});
+ new DiscreteValueValidator(new String[] {"0", "1", "2", "3"});
/**
* Surround sounds formats that are enabled when ENCODED_SURROUND_OUTPUT is set to
@@ -13762,7 +13755,7 @@
public static final String POWER_BUTTON_LONG_PRESS =
"power_button_long_press";
private static final Validator POWER_BUTTON_LONG_PRESS_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+ new InclusiveIntegerRangeValidator(0, 5);
/**
* Overrides internal R.integer.config_veryLongPressOnPowerBehavior.
@@ -13773,7 +13766,7 @@
public static final String POWER_BUTTON_VERY_LONG_PRESS =
"power_button_very_long_press";
private static final Validator POWER_BUTTON_VERY_LONG_PRESS_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 1);
+ new InclusiveIntegerRangeValidator(0, 1);
/**
* Settings to backup. This is here so that it's in the same place as the settings
diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/SettingsValidators.java
deleted file mode 100644
index e4cf9fd..0000000
--- a/core/java/android/provider/SettingsValidators.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.net.Uri;
-import android.text.TextUtils;
-
-import com.android.internal.util.ArrayUtils;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.Locale;
-
-/**
- * This class provides both interface for validation and common validators
- * used to ensure Settings have meaningful values.
- *
- * @hide
- */
-public class SettingsValidators {
-
- public static final Validator BOOLEAN_VALIDATOR =
- new DiscreteValueValidator(new String[] {"0", "1"});
-
- public static final Validator ANY_STRING_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- return true;
- }
- };
-
- public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- try {
- return Integer.parseInt(value) >= 0;
- } catch (NumberFormatException e) {
- return false;
- }
- }
- };
-
- public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- try {
- Integer.parseInt(value);
- return true;
- } catch (NumberFormatException e) {
- return false;
- }
- }
- };
-
- public static final Validator URI_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- try {
- Uri.decode(value);
- return true;
- } catch (IllegalArgumentException e) {
- return false;
- }
- }
- };
-
- /**
- * Does not allow a setting to have a null {@link ComponentName}. Use {@link
- * SettingsValidators#NULLABLE_COMPONENT_NAME_VALIDATOR} instead if a setting can have a
- * nullable {@link ComponentName}.
- */
- public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- return value != null && ComponentName.unflattenFromString(value) != null;
- }
- };
-
- /**
- * Allows a setting to have a null {@link ComponentName}.
- */
- public static final Validator NULLABLE_COMPONENT_NAME_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- return value == null || COMPONENT_NAME_VALIDATOR.validate(value);
- }
- };
-
- public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- return value != null && isStringPackageName(value);
- }
-
- private boolean isStringPackageName(String value) {
- // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers,
- // and underscores ('_'). However, individual package name parts may only
- // start with letters.
- // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
- if (value == null) {
- return false;
- }
- String[] subparts = value.split("\\.");
- boolean isValidPackageName = true;
- for (String subpart : subparts) {
- isValidPackageName &= isSubpartValidForPackageName(subpart);
- if (!isValidPackageName) break;
- }
- return isValidPackageName;
- }
-
- private boolean isSubpartValidForPackageName(String subpart) {
- if (subpart.length() == 0) return false;
- boolean isValidSubpart = Character.isLetter(subpart.charAt(0));
- for (int i = 1; i < subpart.length(); i++) {
- isValidSubpart &= (Character.isLetterOrDigit(subpart.charAt(i))
- || (subpart.charAt(i) == '_'));
- if (!isValidSubpart) break;
- }
- return isValidSubpart;
- }
- };
-
- public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() {
- private static final int MAX_IPV6_LENGTH = 45;
-
- @Override
- public boolean validate(@Nullable String value) {
- if (value == null) {
- return false;
- }
- return value.length() <= MAX_IPV6_LENGTH;
- }
- };
-
- public static final Validator LOCALE_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- if (value == null) {
- return false;
- }
- Locale[] validLocales = Locale.getAvailableLocales();
- for (Locale locale : validLocales) {
- if (value.equals(locale.toString())) {
- return true;
- }
- }
- return false;
- }
- };
-
- /** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */
- public static final Validator JSON_OBJECT_VALIDATOR = (value) -> {
- if (TextUtils.isEmpty(value)) {
- return false;
- }
- try {
- new JSONObject(value);
- return true;
- } catch (JSONException e) {
- return false;
- }
- };
-
- public interface Validator {
- /**
- * Returns whether the input value is valid. Subclasses should handle the case where the
- * input value is {@code null}.
- */
- boolean validate(@Nullable String value);
- }
-
- public static final class DiscreteValueValidator implements Validator {
- private final String[] mValues;
-
- public DiscreteValueValidator(String[] values) {
- mValues = values;
- }
-
- @Override
- public boolean validate(@Nullable String value) {
- return ArrayUtils.contains(mValues, value);
- }
- }
-
- public static final class InclusiveIntegerRangeValidator implements Validator {
- private final int mMin;
- private final int mMax;
-
- public InclusiveIntegerRangeValidator(int min, int max) {
- mMin = min;
- mMax = max;
- }
-
- @Override
- public boolean validate(@Nullable String value) {
- try {
- final int intValue = Integer.parseInt(value);
- return intValue >= mMin && intValue <= mMax;
- } catch (NumberFormatException e) {
- return false;
- }
- }
- }
-
- public static final class InclusiveFloatRangeValidator implements Validator {
- private final float mMin;
- private final float mMax;
-
- public InclusiveFloatRangeValidator(float min, float max) {
- mMin = min;
- mMax = max;
- }
-
- @Override
- public boolean validate(@Nullable String value) {
- try {
- final float floatValue = Float.parseFloat(value);
- return floatValue >= mMin && floatValue <= mMax;
- } catch (NumberFormatException | NullPointerException e) {
- return false;
- }
- }
- }
-
- public static final class ComponentNameListValidator implements Validator {
- private final String mSeparator;
-
- public ComponentNameListValidator(String separator) {
- mSeparator = separator;
- }
-
- @Override
- public boolean validate(@Nullable String value) {
- if (value == null) {
- return false;
- }
- String[] elements = value.split(mSeparator);
- for (String element : elements) {
- if (!COMPONENT_NAME_VALIDATOR.validate(element)) {
- return false;
- }
- }
- return true;
- }
- }
-
- public static final class PackageNameListValidator implements Validator {
- private final String mSeparator;
-
- public PackageNameListValidator(String separator) {
- mSeparator = separator;
- }
-
- @Override
- public boolean validate(@Nullable String value) {
- if (value == null) {
- return false;
- }
- String[] elements = value.split(mSeparator);
- for (String element : elements) {
- if (!PACKAGE_NAME_VALIDATOR.validate(element)) {
- return false;
- }
- }
- return true;
- }
- }
-
- private abstract static class ListValidator implements Validator {
- public boolean validate(@Nullable String value) {
- if (!isEntryValid(value)) {
- return false;
- }
- String[] items = value.split(",");
- for (String item : items) {
- if (!isItemValid(item)) {
- return false;
- }
- }
- return true;
- }
-
- protected abstract boolean isEntryValid(String entry);
-
- protected abstract boolean isItemValid(String item);
- }
-
- /** Ensure a restored value is a string in the format the text-to-speech system handles */
- public static final class TTSListValidator extends ListValidator {
- protected boolean isEntryValid(String entry) {
- return entry != null && entry.length() > 0;
- }
-
- protected boolean isItemValid(String item) {
- String[] parts = item.split(":");
- // Replaces any old language separator (-) with the new one (_)
- return ((parts.length == 2)
- && (parts[0].length() > 0)
- && ANY_STRING_VALIDATOR.validate(parts[0])
- && LOCALE_VALIDATOR.validate(parts[1].replace('-', '_')));
- }
- }
-
- /** Ensure a restored value is suitable to be used as a tile name */
- public static final class TileListValidator extends ListValidator {
- protected boolean isEntryValid(String entry) {
- return entry != null;
- }
-
- protected boolean isItemValid(String item) {
- return item.length() > 0 && ANY_STRING_VALIDATOR.validate(item);
- }
- }
-}
diff --git a/core/java/android/provider/settings/validators/ComponentNameListValidator.java b/core/java/android/provider/settings/validators/ComponentNameListValidator.java
new file mode 100644
index 0000000..b6b867a
--- /dev/null
+++ b/core/java/android/provider/settings/validators/ComponentNameListValidator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+
+/**
+ * Validate a list of compoments.
+ *
+ * @hide
+ */
+public final class ComponentNameListValidator extends ListValidator {
+ public ComponentNameListValidator(String separator) {
+ super(separator);
+ }
+
+ @Override
+ protected boolean isEntryValid(String entry) {
+ return entry != null;
+ }
+
+ @Override
+ protected boolean isItemValid(String item) {
+ return COMPONENT_NAME_VALIDATOR.validate(item);
+ }
+}
diff --git a/core/java/android/provider/settings/validators/DiscreteValueValidator.java b/core/java/android/provider/settings/validators/DiscreteValueValidator.java
new file mode 100644
index 0000000..183651f
--- /dev/null
+++ b/core/java/android/provider/settings/validators/DiscreteValueValidator.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Validate a value exists in an array of known good values
+ *
+ * @hide
+ */
+public final class DiscreteValueValidator implements Validator {
+ private final String[] mValues;
+
+ public DiscreteValueValidator(String[] values) {
+ mValues = values;
+ }
+
+ @Override
+ public boolean validate(@Nullable String value) {
+ return ArrayUtils.contains(mValues, value);
+ }
+}
diff --git a/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java b/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java
new file mode 100644
index 0000000..38400ac
--- /dev/null
+++ b/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate a float value lies within a given (boundary inclusive) range.
+ *
+ * @hide
+ */
+public final class InclusiveFloatRangeValidator implements Validator {
+ private final float mMin;
+ private final float mMax;
+
+ public InclusiveFloatRangeValidator(float min, float max) {
+ mMin = min;
+ mMax = max;
+ }
+
+ @Override
+ public boolean validate(@Nullable String value) {
+ try {
+ final float floatValue = Float.parseFloat(value);
+ return floatValue >= mMin && floatValue <= mMax;
+ } catch (NumberFormatException | NullPointerException e) {
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java b/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
new file mode 100644
index 0000000..e53c252
--- /dev/null
+++ b/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate an integer value lies within a given (boundary inclusive) range.
+ *
+ * @hide
+ */
+public final class InclusiveIntegerRangeValidator implements Validator {
+ private final int mMin;
+ private final int mMax;
+
+ public InclusiveIntegerRangeValidator(int min, int max) {
+ mMin = min;
+ mMax = max;
+ }
+
+ @Override
+ public boolean validate(@Nullable String value) {
+ try {
+ final int intValue = Integer.parseInt(value);
+ return intValue >= mMin && intValue <= mMax;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/provider/settings/validators/ListValidator.java b/core/java/android/provider/settings/validators/ListValidator.java
new file mode 100644
index 0000000..a6001d2
--- /dev/null
+++ b/core/java/android/provider/settings/validators/ListValidator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate the elements in a list.
+ *
+ * @hide
+ */
+abstract class ListValidator implements Validator {
+
+ private String mListSplitRegex;
+
+ ListValidator(String listSplitRegex) {
+ mListSplitRegex = listSplitRegex;
+ }
+
+ public boolean validate(@Nullable String value) {
+ if (!isEntryValid(value)) {
+ return false;
+ }
+ String[] items = value.split(",");
+ for (String item : items) {
+ if (!isItemValid(item)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected abstract boolean isEntryValid(String entry);
+
+ protected abstract boolean isItemValid(String item);
+}
+
diff --git a/core/java/android/provider/settings/validators/PackageNameListValidator.java b/core/java/android/provider/settings/validators/PackageNameListValidator.java
new file mode 100644
index 0000000..bc7fc13
--- /dev/null
+++ b/core/java/android/provider/settings/validators/PackageNameListValidator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+
+/**
+ * Validate a list of package names.
+ *
+ * @hide
+ */
+public final class PackageNameListValidator extends ListValidator {
+ public PackageNameListValidator(String separator) {
+ super(separator);
+ }
+
+ @Override
+ protected boolean isEntryValid(String entry) {
+ return entry != null;
+ }
+
+ @Override
+ protected boolean isItemValid(String item) {
+ return PACKAGE_NAME_VALIDATOR.validate(item);
+ }
+}
diff --git a/core/java/android/provider/settings/validators/SettingsValidators.java b/core/java/android/provider/settings/validators/SettingsValidators.java
new file mode 100644
index 0000000..562c638
--- /dev/null
+++ b/core/java/android/provider/settings/validators/SettingsValidators.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Locale;
+
+/**
+ * This class provides both interface for validation and common validators
+ * used to ensure Settings have meaningful values.
+ *
+ * @hide
+ */
+public class SettingsValidators {
+
+ public static final Validator BOOLEAN_VALIDATOR =
+ new DiscreteValueValidator(new String[] {"0", "1"});
+
+ public static final Validator ANY_STRING_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ return true;
+ }
+ };
+
+ public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ try {
+ return Integer.parseInt(value) >= 0;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ };
+
+ public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ try {
+ Integer.parseInt(value);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ };
+
+ public static final Validator URI_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ try {
+ Uri.decode(value);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+ };
+
+ /**
+ * Does not allow a setting to have a null {@link ComponentName}. Use {@link
+ * SettingsValidators#NULLABLE_COMPONENT_NAME_VALIDATOR} instead if a setting can have a
+ * nullable {@link ComponentName}.
+ */
+ public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ return value != null && ComponentName.unflattenFromString(value) != null;
+ }
+ };
+
+ /**
+ * Allows a setting to have a null {@link ComponentName}.
+ */
+ public static final Validator NULLABLE_COMPONENT_NAME_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ return value == null || COMPONENT_NAME_VALIDATOR.validate(value);
+ }
+ };
+
+ public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ return value != null && isStringPackageName(value);
+ }
+
+ private boolean isStringPackageName(String value) {
+ // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers,
+ // and underscores ('_'). However, individual package name parts may only
+ // start with letters.
+ // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
+ if (value == null) {
+ return false;
+ }
+ String[] subparts = value.split("\\.");
+ boolean isValidPackageName = true;
+ for (String subpart : subparts) {
+ isValidPackageName &= isSubpartValidForPackageName(subpart);
+ if (!isValidPackageName) break;
+ }
+ return isValidPackageName;
+ }
+
+ private boolean isSubpartValidForPackageName(String subpart) {
+ if (subpart.length() == 0) return false;
+ boolean isValidSubpart = Character.isLetter(subpart.charAt(0));
+ for (int i = 1; i < subpart.length(); i++) {
+ isValidSubpart &= (Character.isLetterOrDigit(subpart.charAt(i))
+ || (subpart.charAt(i) == '_'));
+ if (!isValidSubpart) break;
+ }
+ return isValidSubpart;
+ }
+ };
+
+ public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() {
+ private static final int MAX_IPV6_LENGTH = 45;
+
+ @Override
+ public boolean validate(@Nullable String value) {
+ if (value == null) {
+ return false;
+ }
+ return value.length() <= MAX_IPV6_LENGTH;
+ }
+ };
+
+ public static final Validator LOCALE_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ if (value == null) {
+ return false;
+ }
+ Locale[] validLocales = Locale.getAvailableLocales();
+ for (Locale locale : validLocales) {
+ if (value.equals(locale.toString())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ /** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */
+ public static final Validator JSON_OBJECT_VALIDATOR = (value) -> {
+ if (TextUtils.isEmpty(value)) {
+ return false;
+ }
+ try {
+ new JSONObject(value);
+ return true;
+ } catch (JSONException e) {
+ return false;
+ }
+ };
+
+ public static final Validator TTS_LIST_VALIDATOR = new TTSListValidator();
+
+ public static final Validator TILE_LIST_VALIDATOR = new TileListValidator();
+}
diff --git a/core/java/android/provider/settings/validators/TTSListValidator.java b/core/java/android/provider/settings/validators/TTSListValidator.java
new file mode 100644
index 0000000..6c73471
--- /dev/null
+++ b/core/java/android/provider/settings/validators/TTSListValidator.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
+
+/**
+ * Ensure a restored value is a string in the format the text-to-speech system handles
+ *
+ * @hide
+ */
+final class TTSListValidator extends ListValidator {
+
+ TTSListValidator() {
+ super(",");
+ }
+
+ protected boolean isEntryValid(String entry) {
+ return entry != null && entry.length() > 0;
+ }
+
+ protected boolean isItemValid(String item) {
+ String[] parts = item.split(":");
+ // Replaces any old language separator (-) with the new one (_)
+ return ((parts.length == 2)
+ && (parts[0].length() > 0)
+ && ANY_STRING_VALIDATOR.validate(parts[0])
+ && LOCALE_VALIDATOR.validate(parts[1].replace('-', '_')));
+ }
+}
diff --git a/core/java/android/provider/settings/validators/TileListValidator.java b/core/java/android/provider/settings/validators/TileListValidator.java
new file mode 100644
index 0000000..c696442
--- /dev/null
+++ b/core/java/android/provider/settings/validators/TileListValidator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+
+/**
+ * Ensure a restored value is suitable to be used as a tile name
+ *
+ * @hide
+ */
+final class TileListValidator extends ListValidator {
+ TileListValidator() {
+ super(",");
+ }
+
+ protected boolean isEntryValid(String entry) {
+ return entry != null;
+ }
+
+ protected boolean isItemValid(String item) {
+ return item.length() > 0 && ANY_STRING_VALIDATOR.validate(item);
+ }
+}
diff --git a/core/java/android/provider/settings/validators/Validator.java b/core/java/android/provider/settings/validators/Validator.java
new file mode 100644
index 0000000..393a03d
--- /dev/null
+++ b/core/java/android/provider/settings/validators/Validator.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Interface for a settings value validator.
+ *
+ * @hide
+ */
+public interface Validator {
+ /**
+ * Returns whether the input value is valid. Subclasses should handle the case where the
+ * input value is {@code null}.
+ */
+ boolean validate(@Nullable String value);
+}
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index 8b5659b..3a1df3e 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -60,9 +60,19 @@
}
public synchronized void dump(PrintWriter pw) {
+ dump("", pw);
+ }
+
+ /**
+ * Dumps the content of local log to print writer with each log entry predeced with indent
+ *
+ * @param indent indent that precedes each log entry
+ * @param pw printer writer to write into
+ */
+ public synchronized void dump(String indent, PrintWriter pw) {
Iterator<String> itr = mLog.iterator();
while (itr.hasNext()) {
- pw.println(itr.next());
+ pw.printf("%s%s\n", indent, itr.next());
}
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 00206fc..faeecda 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -164,6 +164,8 @@
public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share";
private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
+
+ private boolean mIsAppPredictorComponentAvailable;
private AppPredictor mAppPredictor;
private AppPredictor.Callback mAppPredictorCallback;
private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
@@ -617,6 +619,9 @@
.addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
.addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
+ // This is the only place this value is being set. Effectively final.
+ mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable();
+
AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
if (appPredictor != null) {
mDirectShareAppTargetCache = new HashMap<>();
@@ -707,6 +712,32 @@
}
/**
+ * Returns true if app prediction service is defined and the component exists on device.
+ */
+ private boolean isAppPredictionServiceAvailable() {
+ final String appPredictionServiceName =
+ getString(R.string.config_defaultAppPredictionService);
+ if (appPredictionServiceName == null) {
+ return false;
+ }
+ final ComponentName appPredictionComponentName =
+ ComponentName.unflattenFromString(appPredictionServiceName);
+ if (appPredictionComponentName == null) {
+ return false;
+ }
+
+ // Check if the app prediction component actually exists on the device.
+ Intent intent = new Intent();
+ intent.setComponent(appPredictionComponentName);
+ if (getPackageManager().resolveService(intent, PackageManager.MATCH_ALL) == null) {
+ Log.e(TAG, "App prediction service is defined, but does not exist: "
+ + appPredictionServiceName);
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Check if the profile currently used is a work profile.
* @return true if it is work profile, false if it is parent profile (or no work profile is
* set up)
@@ -1693,6 +1724,9 @@
@Nullable
private AppPredictor getAppPredictor() {
+ if (!mIsAppPredictorComponentAvailable) {
+ return null;
+ }
if (mAppPredictor == null
&& getPackageManager().getAppPredictionServicePackageName() != null) {
final IntentFilter filter = getTargetIntentFilter();
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5cbf81c..096bf69 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -497,11 +497,8 @@
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- std::vector<uint8_t> byteData(parcel->dataSize());
- memcpy(byteData.data(), parcel->data(), parcel->dataSize());
-
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- transaction->setMetadata(ctrl, id, std::move(byteData));
+ transaction->setMetadata(ctrl, id, *parcel);
}
static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 40c2cbe..ceccd0d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4286,4 +4286,9 @@
<!-- The list of packages to automatically opt out of refresh rates higher than 60hz because
of known compatibility issues. -->
<string-array name="config_highRefreshRateBlacklist"></string-array>
+
+ <!-- Whether or not to hide the navigation bar when the soft keyboard is visible in order to
+ create additional screen real estate outside beyond the keyboard. Note that the user needs
+ to have a confirmed way to dismiss the keyboard when desired. -->
+ <bool name="config_automotiveHideNavBarForKeyboard">false</bool>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f7ae453..ae5fd83 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3847,4 +3847,6 @@
<java-symbol type="string" name="config_factoryResetPackage" />
<java-symbol type="array" name="config_highRefreshRateBlacklist" />
+ <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />
+
</resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 8a21444..97b7ae9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -720,7 +720,6 @@
Settings.Secure.BIOMETRIC_DEBUG_ENABLED,
Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED,
- Settings.Secure.FACE_UNLOCK_EDUCATION_INFO_DISPLAYED,
Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED);
@Test
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
similarity index 85%
rename from core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
rename to core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
index eea8c83..5f042d3 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider;
+package android.provider.settings.validators;
import static com.google.common.truth.Truth.assertThat;
@@ -23,7 +23,7 @@
import static org.junit.Assert.fail;
import android.platform.test.annotations.Presubmit;
-import android.provider.SettingsValidators.Validator;
+import android.provider.Settings;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -138,7 +138,7 @@
@Test
public void testDiscreteValueValidator() {
String[] beerTypes = new String[]{"Ale", "American IPA", "Stout"};
- Validator v = new SettingsValidators.DiscreteValueValidator(beerTypes);
+ Validator v = new DiscreteValueValidator(beerTypes);
assertTrue(v.validate("Ale"));
assertTrue(v.validate("American IPA"));
assertTrue(v.validate("Stout"));
@@ -148,14 +148,14 @@
@Test
public void testDiscreteValueValidator_onNullValue_returnsFalse() {
String[] discreteTypes = new String[]{"Type1", "Type2"};
- Validator v = new SettingsValidators.DiscreteValueValidator(discreteTypes);
+ Validator v = new DiscreteValueValidator(discreteTypes);
assertFalse(v.validate(null));
}
@Test
public void testInclusiveIntegerRangeValidator() {
- Validator v = new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+ Validator v = new InclusiveIntegerRangeValidator(0, 5);
assertTrue(v.validate("0"));
assertTrue(v.validate("2"));
assertTrue(v.validate("5"));
@@ -165,14 +165,14 @@
@Test
public void testInclusiveIntegerRangeValidator_onNullValue_returnsFalse() {
- Validator v = new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+ Validator v = new InclusiveIntegerRangeValidator(0, 5);
assertFalse(v.validate(null));
}
@Test
public void testInclusiveFloatRangeValidator() {
- Validator v = new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 5.0f);
+ Validator v = new InclusiveFloatRangeValidator(0.0f, 5.0f);
assertTrue(v.validate("0.0"));
assertTrue(v.validate("2.0"));
assertTrue(v.validate("5.0"));
@@ -182,14 +182,14 @@
@Test
public void testInclusiveFloatRangeValidator_onNullValue_returnsFalse() {
- Validator v = new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 5.0f);
+ Validator v = new InclusiveFloatRangeValidator(0.0f, 5.0f);
assertFalse(v.validate(null));
}
@Test
public void testComponentNameListValidator() {
- Validator v = new SettingsValidators.ComponentNameListValidator(",");
+ Validator v = new ComponentNameListValidator(",");
assertTrue(v.validate("com.android.localtransport/.LocalTransport,"
+ "com.google.android.gms/.backup.migrate.service.D2dTransport"));
assertFalse(v.validate("com.google.5android,android"));
@@ -197,21 +197,21 @@
@Test
public void testComponentNameListValidator_onNullValue_returnsFalse() {
- Validator v = new SettingsValidators.ComponentNameListValidator(",");
+ Validator v = new ComponentNameListValidator(",");
assertFalse(v.validate(null));
}
@Test
public void testPackageNameListValidator() {
- Validator v = new SettingsValidators.PackageNameListValidator(",");
+ Validator v = new PackageNameListValidator(",");
assertTrue(v.validate("com.android.localtransport.LocalTransport,com.google.android.gms"));
assertFalse(v.validate("5com.android.internal.backup.LocalTransport,android"));
}
@Test
public void testPackageNameListValidator_onNullValue_returnsFalse() {
- Validator v = new SettingsValidators.PackageNameListValidator(",");
+ Validator v = new PackageNameListValidator(",");
assertFalse(v.validate(null));
}
@@ -256,51 +256,41 @@
@Test
public void testTTSListValidator_withValidInput_returnsTrue() {
- Validator v = new SettingsValidators.TTSListValidator();
-
- assertTrue(v.validate("com.foo.ttsengine:en-US,com.bar.ttsengine:es_ES"));
+ assertTrue(
+ SettingsValidators.TTS_LIST_VALIDATOR.validate(
+ "com.foo.ttsengine:en-US,com.bar.ttsengine:es_ES"));
}
@Test
public void testTTSListValidator_withInvalidInput_returnsFalse() {
- Validator v = new SettingsValidators.TTSListValidator();
-
- assertFalse(v.validate("com.foo.ttsengine:eng-USA,INVALID"));
+ assertFalse(
+ SettingsValidators.TTS_LIST_VALIDATOR.validate(
+ "com.foo.ttsengine:eng-USA,INVALID"));
}
@Test
public void testTTSListValidator_withEmptyInput_returnsFalse() {
- Validator v = new SettingsValidators.TTSListValidator();
-
- assertFalse(v.validate(""));
+ assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(""));
}
@Test
public void testTTSListValidator_withNullInput_returnsFalse() {
- Validator v = new SettingsValidators.TTSListValidator();
-
- assertFalse(v.validate(null));
+ assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(null));
}
@Test
public void testTileListValidator_withValidInput_returnsTrue() {
- Validator v = new SettingsValidators.TileListValidator();
-
- assertTrue(v.validate("1,2,3,4"));
+ assertTrue(SettingsValidators.TILE_LIST_VALIDATOR.validate("1,2,3,4"));
}
@Test
public void testTileListValidator_withMissingValue_returnsFalse() {
- Validator v = new SettingsValidators.TileListValidator();
-
- assertFalse(v.validate("1,,3"));
+ assertFalse(SettingsValidators.TILE_LIST_VALIDATOR.validate("1,,3"));
}
@Test
public void testTileListValidator_withNullInput_returnsFalse() {
- Validator v = new SettingsValidators.TileListValidator();
-
- assertFalse(v.validate(null));
+ assertFalse(SettingsValidators.TILE_LIST_VALIDATOR.validate(null));
}
@Test
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 89d3cc4..16f2917 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -20,6 +20,7 @@
],
shared_libs: [
+ "libbinder",
"libcutils",
"liblog",
"libutils",
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index c1868d3..fd386e9 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -245,7 +245,8 @@
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
- | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
+ | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID
+ | DIRTY_ICON_STYLE))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -274,6 +275,21 @@
update.state.transformationMatrix.dtdy);
}
+ if (wantSurfaceVisibleAndDrawn
+ && (becomingVisible
+ || (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) {
+ Parcel p;
+ p.writeInt32(update.state.icon.style);
+ p.writeFloat(update.state.icon.hotSpotX);
+ p.writeFloat(update.state.icon.hotSpotY);
+
+ // Pass cursor metadata in the sprite surface so that when Android is running as a
+ // client OS (e.g. ARC++) the host OS can get the requested cursor metadata and
+ // update mouse cursor in the host OS.
+ t.setMetadata(
+ update.state.surfaceControl, METADATA_MOUSE_CURSOR, p);
+ }
+
int32_t surfaceLayer = mOverlayLayer + update.state.layer;
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
@@ -397,9 +413,14 @@
} else {
dirty = DIRTY_BITMAP;
}
+
+ if (mLocked.state.icon.style != icon.style) {
+ mLocked.state.icon.style = icon.style;
+ dirty |= DIRTY_ICON_STYLE;
+ }
} else if (mLocked.state.icon.isValid()) {
mLocked.state.icon.bitmap.reset();
- dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
+ dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE;
} else {
return; // setting to invalid icon and already invalid so nothing to do
}
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 5b216f5..79a904f 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -55,11 +55,12 @@
* Icon that a sprite displays, including its hotspot.
*/
struct SpriteIcon {
- inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
- inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
- bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
+ inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { }
+ inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
+ bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
SkBitmap bitmap;
+ int32_t style;
float hotSpotX;
float hotSpotY;
@@ -69,11 +70,12 @@
bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
0, 0);
}
- return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
+ return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY);
}
inline void reset() {
bitmap.reset();
+ style = 0;
hotSpotX = 0;
hotSpotY = 0;
}
@@ -149,15 +151,15 @@
SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
/* Creates a new sprite, initially invisible. */
- sp<Sprite> createSprite();
+ virtual sp<Sprite> createSprite();
/* Opens or closes a transaction to perform a batch of sprite updates as part of
* a single operation such as setPosition and setAlpha. It is not necessary to
* open a transaction when updating a single property.
* Calls to openTransaction() nest and must be matched by an equal number
* of calls to closeTransaction(). */
- void openTransaction();
- void closeTransaction();
+ virtual void openTransaction();
+ virtual void closeTransaction();
private:
enum {
@@ -174,6 +176,7 @@
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
DIRTY_DISPLAY_ID = 1 << 7,
+ DIRTY_ICON_STYLE = 1 << 8,
};
/* Describes the state of a sprite.
diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING
new file mode 100644
index 0000000..fe74c62
--- /dev/null
+++ b/libs/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libinputservice_test"
+ }
+ ]
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
new file mode 100644
index 0000000..e83b2a7
--- /dev/null
+++ b/libs/input/tests/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2019 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.
+
+cc_test {
+ name: "libinputservice_test",
+ srcs: [
+ "PointerController_test.cpp",
+ ],
+ shared_libs: [
+ "libinputservice",
+ "libgui",
+ "libhwui",
+ "libutils",
+ ],
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ ],
+ header_libs: [
+ "libbase_headers",
+ "libinputflinger_headers",
+ ],
+ include_dirs: [
+ "frameworks/base/libs",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
new file mode 100644
index 0000000..92efb4e
--- /dev/null
+++ b/libs/input/tests/PointerController_test.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "mocks/MockSprite.h"
+#include "mocks/MockSpriteController.h"
+
+#include <input/PointerController.h>
+#include <input/SpriteController.h>
+
+#include <atomic>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+
+enum TestCursorType {
+ CURSOR_TYPE_DEFAULT = 0,
+ CURSOR_TYPE_HOVER,
+ CURSOR_TYPE_TOUCH,
+ CURSOR_TYPE_ANCHOR,
+ CURSOR_TYPE_ADDITIONAL_1,
+ CURSOR_TYPE_ADDITIONAL_2,
+ CURSOR_TYPE_CUSTOM = -1,
+};
+
+using ::testing::AllOf;
+using ::testing::Field;
+using ::testing::NiceMock;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::Test;
+
+std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
+ return std::make_pair(type * 10, type * 10 + 5);
+}
+
+class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
+public:
+ virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
+ virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
+ virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
+ std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
+ virtual int32_t getDefaultPointerIconId() override;
+ virtual int32_t getCustomPointerIconId() override;
+
+private:
+ void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
+};
+
+void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
+ loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
+}
+
+void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
+ int32_t) {
+ loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
+ loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
+ loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
+}
+
+void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
+ std::map<int32_t, SpriteIcon>* outResources,
+ std::map<int32_t, PointerAnimation>* outAnimationResources,
+ int32_t) {
+ SpriteIcon icon;
+ PointerAnimation anim;
+
+ for (int32_t cursorType : {CURSOR_TYPE_ADDITIONAL_1, CURSOR_TYPE_ADDITIONAL_2}) {
+ loadPointerIconForType(&icon, cursorType);
+ anim.animationFrames.push_back(icon);
+ anim.durationPerFrame = 10;
+ (*outResources)[cursorType] = icon;
+ (*outAnimationResources)[cursorType] = anim;
+ }
+}
+
+int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
+ return CURSOR_TYPE_DEFAULT;
+}
+
+int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
+ return CURSOR_TYPE_CUSTOM;
+}
+
+void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
+ icon->style = type;
+ std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
+ icon->hotSpotX = hotSpot.first;
+ icon->hotSpotY = hotSpot.second;
+}
+
+class PointerControllerTest : public Test {
+protected:
+ PointerControllerTest();
+ ~PointerControllerTest();
+
+ sp<MockSprite> mPointerSprite;
+ sp<MockPointerControllerPolicyInterface> mPolicy;
+ sp<MockSpriteController> mSpriteController;
+ sp<PointerController> mPointerController;
+
+private:
+ void loopThread();
+
+ std::atomic<bool> mRunning = true;
+ class MyLooper : public Looper {
+ public:
+ MyLooper() : Looper(false) {}
+ ~MyLooper() = default;
+ };
+ sp<MyLooper> mLooper;
+ std::thread mThread;
+};
+
+PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
+ mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
+
+ mSpriteController = new NiceMock<MockSpriteController>(mLooper);
+ mPolicy = new MockPointerControllerPolicyInterface();
+
+ EXPECT_CALL(*mSpriteController, createSprite())
+ .WillOnce(Return(mPointerSprite));
+
+ mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+
+ DisplayViewport viewport;
+ viewport.displayId = ADISPLAY_ID_DEFAULT;
+ viewport.logicalRight = 1600;
+ viewport.logicalBottom = 1200;
+ viewport.physicalRight = 800;
+ viewport.physicalBottom = 600;
+ viewport.deviceWidth = 400;
+ viewport.deviceHeight = 300;
+ mPointerController->setDisplayViewport(viewport);
+}
+
+PointerControllerTest::~PointerControllerTest() {
+ mRunning.store(false, std::memory_order_relaxed);
+ mThread.join();
+}
+
+void PointerControllerTest::loopThread() {
+ Looper::setForThread(mLooper);
+
+ while (mRunning.load(std::memory_order_relaxed)) {
+ mLooper->pollOnce(100);
+ }
+}
+
+TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
+ mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+ std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
+ EXPECT_CALL(*mPointerSprite, setVisible(true));
+ EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+ EXPECT_CALL(*mPointerSprite, setIcon(
+ AllOf(
+ Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
+ Field(&SpriteIcon::hotSpotX, hotspot.first),
+ Field(&SpriteIcon::hotSpotY, hotspot.second))));
+ mPointerController->reloadPointerResources();
+}
+
+TEST_F(PointerControllerTest, updatePointerIcon) {
+ mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+ int32_t type = CURSOR_TYPE_ADDITIONAL_1;
+ std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
+ EXPECT_CALL(*mPointerSprite, setVisible(true));
+ EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+ EXPECT_CALL(*mPointerSprite, setIcon(
+ AllOf(
+ Field(&SpriteIcon::style, type),
+ Field(&SpriteIcon::hotSpotX, hotspot.first),
+ Field(&SpriteIcon::hotSpotY, hotspot.second))));
+ mPointerController->updatePointerIcon(type);
+}
+
+TEST_F(PointerControllerTest, setCustomPointerIcon) {
+ mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+ int32_t style = CURSOR_TYPE_CUSTOM;
+ float hotSpotX = 15;
+ float hotSpotY = 20;
+
+ SpriteIcon icon;
+ icon.style = style;
+ icon.hotSpotX = hotSpotX;
+ icon.hotSpotY = hotSpotY;
+
+ EXPECT_CALL(*mPointerSprite, setVisible(true));
+ EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+ EXPECT_CALL(*mPointerSprite, setIcon(
+ AllOf(
+ Field(&SpriteIcon::style, style),
+ Field(&SpriteIcon::hotSpotX, hotSpotX),
+ Field(&SpriteIcon::hotSpotY, hotSpotY))));
+ mPointerController->setCustomPointerIcon(icon);
+}
+
+} // namespace android
diff --git a/libs/input/tests/mocks/MockSprite.h b/libs/input/tests/mocks/MockSprite.h
new file mode 100644
index 0000000..013b79c
--- /dev/null
+++ b/libs/input/tests/mocks/MockSprite.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef _MOCK_SPRITE_H
+#define _MOCK_SPRITE_H
+
+#include <input/SpriteController.h>
+
+#include <gmock/gmock.h>
+
+namespace android {
+
+class MockSprite : public Sprite {
+public:
+ virtual ~MockSprite() = default;
+
+ MOCK_METHOD(void, setIcon, (const SpriteIcon& icon), (override));
+ MOCK_METHOD(void, setVisible, (bool), (override));
+ MOCK_METHOD(void, setPosition, (float, float), (override));
+ MOCK_METHOD(void, setLayer, (int32_t), (override));
+ MOCK_METHOD(void, setAlpha, (float), (override));
+ MOCK_METHOD(void, setTransformationMatrix, (const SpriteTransformationMatrix&), (override));
+ MOCK_METHOD(void, setDisplayId, (int32_t), (override));
+};
+
+} // namespace android
+
+#endif // _MOCK_SPRITE_H
diff --git a/libs/input/tests/mocks/MockSpriteController.h b/libs/input/tests/mocks/MockSpriteController.h
new file mode 100644
index 0000000..a034f66
--- /dev/null
+++ b/libs/input/tests/mocks/MockSpriteController.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef _MOCK_SPRITE_CONTROLLER_H
+#define _MOCK_SPRITE_CONTROLLER_H
+
+#include "MockSprite.h"
+
+#include <input/SpriteController.h>
+
+namespace android {
+
+class MockSpriteController : public SpriteController {
+
+public:
+ MockSpriteController(sp<Looper> looper) : SpriteController(looper, 0) {}
+ ~MockSpriteController() {}
+
+ MOCK_METHOD(sp<Sprite>, createSprite, (), (override));
+ MOCK_METHOD(void, openTransaction, (), (override));
+ MOCK_METHOD(void, closeTransaction, (), (override));
+};
+
+} // namespace android
+
+#endif // _MOCK_SPRITE_CONTROLLER_H
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 148ffaf..55583d5 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -37,6 +37,7 @@
import libcore.io.Streams;
import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
@@ -1866,14 +1867,17 @@
FileInputStream in = null;
FileOutputStream out = null;
+ File originalFile = null;
+ if (mFilename != null) {
+ originalFile = new File(mFilename);
+ }
File tempFile = null;
try {
// Move the original file to temporary file.
if (mFilename != null) {
tempFile = new File(mFilename + ".tmp");
- File originalFile = new File(mFilename);
if (!originalFile.renameTo(tempFile)) {
- throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
+ throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath());
}
} else if (mSeekableFileDescriptor != null) {
tempFile = File.createTempFile("temp", "jpg");
@@ -1882,8 +1886,8 @@
out = new FileOutputStream(tempFile);
Streams.copy(in, out);
}
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
+ } catch (Exception e) {
+ throw new IOException("Failed to copy original file to temp file", e);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
@@ -1900,9 +1904,18 @@
Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
out = new FileOutputStream(mSeekableFileDescriptor);
}
- saveJpegAttributes(in, out);
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
+ try (BufferedInputStream bufferedIn = new BufferedInputStream(in);
+ BufferedOutputStream bufferedOut = new BufferedOutputStream(out)) {
+ saveJpegAttributes(bufferedIn, bufferedOut);
+ }
+ } catch (Exception e) {
+ if (mFilename != null) {
+ if (!tempFile.renameTo(originalFile)) {
+ throw new IOException("Couldn't restore original file: "
+ + originalFile.getAbsolutePath());
+ }
+ }
+ throw new IOException("Failed to save new file", e);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 97fbea6..e95103b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -32,7 +32,10 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
import android.util.Log;
+import android.view.Display;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -87,8 +90,7 @@
/**
* A status bar (and navigation bar) tailored for the automotive use case.
*/
-public class CarStatusBar extends StatusBar implements
- CarBatteryController.BatteryViewHandler {
+public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
private static final String TAG = "CarStatusBar";
// used to calculate how fast to open or close the window
private static final float DEFAULT_FLING_VELOCITY = 0;
@@ -169,6 +171,9 @@
private boolean mIsSwipingVerticallyToClose;
// Whether heads-up notifications should be shown when shade is open.
private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
+ // If the nav bar should be hidden when the soft keyboard is visible.
+ private boolean mHideNavBarForKeyboard;
+ private boolean mBottomNavBarVisible;
private final CarPowerStateListener mCarPowerStateListener =
(int state) -> {
@@ -190,6 +195,12 @@
// builds the nav bar
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
+
+ // Keyboard related setup, before nav bars are created.
+ mHideNavBarForKeyboard = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
+ mBottomNavBarVisible = false;
+
super.start();
mTaskStackListener = new TaskStackListenerImpl();
mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
@@ -720,6 +731,13 @@
buildNavBarContent();
attachNavBarWindows();
+ // Try setting up the initial state of the nav bar if applicable.
+ if (result != null) {
+ setImeWindowStatus(Display.DEFAULT_DISPLAY, result.mImeToken,
+ result.mImeWindowVis, result.mImeBackDisposition,
+ result.mShowImeSwitcher);
+ }
+
// There has been a car customized nav bar on the default display, so just create nav bars
// on external displays.
mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */, result);
@@ -758,22 +776,33 @@
}
- private void attachNavBarWindows() {
-
- if (mShowBottom) {
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
- 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("CarNavigationBar");
- lp.windowAnimations = 0;
- mWindowManager.addView(mNavigationBarWindow, lp);
+ /**
+ * We register for soft keyboard visibility events such that we can hide the navigation bar
+ * giving more screen space to the IME. Note: this is optional and controlled by
+ * {@code com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard}.
+ */
+ @Override
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher) {
+ if (!mHideNavBarForKeyboard) {
+ return;
}
+ if (mContext.getDisplay().getDisplayId() != displayId) {
+ return;
+ }
+
+ boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
+ if (!isKeyboardVisible) {
+ attachBottomNavBarWindow();
+ } else {
+ detachBottomNavBarWindow();
+ }
+ }
+
+ private void attachNavBarWindows() {
+ attachBottomNavBarWindow();
+
if (mShowLeft) {
int width = mContext.getResources().getDimensionPixelSize(
R.dimen.car_left_navigation_bar_width);
@@ -808,7 +837,41 @@
rightlp.gravity = Gravity.RIGHT;
mWindowManager.addView(mRightNavigationBarWindow, rightlp);
}
+ }
+ private void attachBottomNavBarWindow() {
+ if (!mShowBottom) {
+ return;
+ }
+
+ if (mBottomNavBarVisible) {
+ return;
+ }
+ mBottomNavBarVisible = true;
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ 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("CarNavigationBar");
+ lp.windowAnimations = 0;
+ mWindowManager.addView(mNavigationBarWindow, lp);
+ }
+
+ private void detachBottomNavBarWindow() {
+ if (!mShowBottom) {
+ return;
+ }
+
+ if (!mBottomNavBarVisible) {
+ return;
+ }
+ mBottomNavBarVisible = false;
+ mWindowManager.removeView(mNavigationBarWindow);
}
private void buildBottomBar(int layout) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 2286f4c..36e945f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -34,7 +34,7 @@
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.provider.Settings;
-import android.provider.SettingsValidators.Validator;
+import android.provider.settings.validators.Validator;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.BackupUtils;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index c1aa0f9..e492e28 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -70,7 +70,7 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-import android.provider.SettingsValidators;
+import android.provider.settings.validators.Validator;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -1717,7 +1717,7 @@
}
private void validateSystemSettingValue(String name, String value) {
- SettingsValidators.Validator validator = Settings.System.VALIDATORS.get(name);
+ Validator validator = Settings.System.VALIDATORS.get(name);
if (validator != null && !validator.validate(value)) {
throw new IllegalArgumentException("Invalid value: " + value
+ " for setting: " + name);
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 58e1d47..8c0108d 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -352,10 +352,10 @@
private final BugreportInfo mInfo;
- BugreportCallbackImpl(String name) {
+ BugreportCallbackImpl(String name, @Nullable String title, @Nullable String description) {
// pid not used in this workflow, so setting default = 0
mInfo = new BugreportInfo(mContext, 0 /* pid */, name,
- 100 /* max progress*/);
+ 100 /* max progress*/, title, description);
}
@Override
@@ -578,6 +578,8 @@
}
int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE,
BugreportParams.BUGREPORT_MODE_INTERACTIVE);
+ String shareTitle = intent.getStringExtra(EXTRA_TITLE);
+ String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
ParcelFileDescriptor screenshotFd = createReadWriteFile(BUGREPORT_DIR,
bugreportName + ".png");
@@ -595,7 +597,8 @@
+ " bugreport file fd: " + bugreportFd
+ " screenshot file fd: " + screenshotFd);
- BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName);
+ BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName,
+ shareTitle, shareDescription);
try {
mBugreportManager.startBugreport(bugreportFd, screenshotFd,
new BugreportParams(bugreportType), executor, bugreportCallback);
@@ -982,7 +985,10 @@
}
screenshotFile = null;
}
- onBugreportFinished(id, bugreportFile, screenshotFile, info.title, info.description, max);
+ // TODO: Since we are passing id to the function, it should be able to find the info linked
+ // to the id and therefore use the value of shareTitle and shareDescription.
+ onBugreportFinished(id, bugreportFile, screenshotFile, info.shareTitle,
+ info.shareDescription, max);
}
@@ -1844,6 +1850,14 @@
String title;
/**
+ * One-line summary of the bug; when set, will be used as the subject of the
+ * {@link Intent#ACTION_SEND_MULTIPLE} intent. This is the predefined title which is
+ * set initially when the request to take a bugreport is made. This overrides any changes
+ * in the title that the user makes after the bugreport starts.
+ */
+ String shareTitle;
+
+ /**
* User-provided, detailed description of the bugreport; when set, will be added to the body
* of the {@link Intent#ACTION_SEND_MULTIPLE} intent.
*/
@@ -1906,7 +1920,9 @@
int screenshotCounter;
/**
- * Descriptive text that will be shown to the user in the notification message.
+ * Descriptive text that will be shown to the user in the notification message. This is the
+ * predefined description which is set initially when the request to take a bugreport is
+ * made.
*/
String shareDescription;
@@ -1914,18 +1930,21 @@
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
*/
BugreportInfo(Context context, int id, int pid, String name, int max) {
- this(context, pid, name, max);
+ this(context, pid, name, max, null, null);
this.id = id;
}
/**
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
*/
- BugreportInfo(Context context, int pid, String name, int max) {
+ BugreportInfo(Context context, int pid, String name, int max, @Nullable String shareTitle,
+ @Nullable String shareDescription) {
this.context = context;
this.pid = pid;
this.name = name;
this.max = this.realMax = max;
+ this.shareTitle = shareTitle == null ? "" : shareTitle;
+ this.shareDescription = shareDescription == null ? "" : shareDescription;
}
/**
@@ -2019,6 +2038,7 @@
.append("\n\taddingDetailsToZip: ").append(addingDetailsToZip)
.append(" addedDetailsToZip: ").append(addedDetailsToZip)
.append("\n\tshareDescription: ").append(shareDescription)
+ .append("\n\tshareTitle: ").append(shareTitle)
.toString();
}
@@ -2046,6 +2066,7 @@
finished = in.readInt() == 1;
screenshotCounter = in.readInt();
shareDescription = in.readString();
+ shareTitle = in.readString();
}
@Override
@@ -2071,6 +2092,7 @@
dest.writeInt(finished ? 1 : 0);
dest.writeInt(screenshotCounter);
dest.writeString(shareDescription);
+ dest.writeString(shareTitle);
}
@Override
diff --git a/services/core/java/com/android/server/AnimationThread.java b/services/core/java/com/android/server/AnimationThread.java
index c86042b..c607b1e 100644
--- a/services/core/java/com/android/server/AnimationThread.java
+++ b/services/core/java/com/android/server/AnimationThread.java
@@ -21,6 +21,8 @@
import android.os.Handler;
import android.os.Trace;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Thread for handling all legacy window animations, or anything that's directly impacting
* animations like starting windows or traversals.
@@ -55,4 +57,20 @@
return sHandler;
}
}
+
+ /**
+ * Disposes current animation thread if it's initialized. Should only be used in tests to set up
+ * a new environment.
+ */
+ @VisibleForTesting
+ public static void dispose() {
+ synchronized (DisplayThread.class) {
+ if (sInstance == null) {
+ return;
+ }
+
+ getHandler().runWithScissors(() -> sInstance.quit(), 0 /* timeout */);
+ sInstance = null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/DisplayThread.java b/services/core/java/com/android/server/DisplayThread.java
index 85c799c..a07ade0 100644
--- a/services/core/java/com/android/server/DisplayThread.java
+++ b/services/core/java/com/android/server/DisplayThread.java
@@ -20,6 +20,8 @@
import android.os.Process;
import android.os.Trace;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Shared singleton foreground thread for the system. This is a thread for
* operations that affect what's on the display, which needs to have a minimum
@@ -58,4 +60,20 @@
return sHandler;
}
}
+
+ /**
+ * Disposes current display thread if it's initialized. Should only be used in tests to set up a
+ * new environment.
+ */
+ @VisibleForTesting
+ public static void dispose() {
+ synchronized (DisplayThread.class) {
+ if (sInstance == null) {
+ return;
+ }
+
+ getHandler().runWithScissors(() -> sInstance.quit(), 0 /* timeout */);
+ sInstance = null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 19c818f..2292889 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -282,6 +282,7 @@
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
@@ -378,7 +379,7 @@
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -437,6 +438,10 @@
// need not be the case.
public static final String ACTION_TRIGGER_IDLE = "com.android.server.ACTION_TRIGGER_IDLE";
+ private static final String INTENT_BUGREPORT_REQUESTED =
+ "com.android.internal.intent.action.BUGREPORT_REQUESTED";
+ private static final String SHELL_APP_PACKAGE = "com.android.shell";
+
/** Control over CPU and battery monitoring */
// write battery stats every 30 minutes.
static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
@@ -555,6 +560,10 @@
OomAdjuster mOomAdjuster;
final LowMemDetector mLowMemDetector;
+ static final String EXTRA_TITLE = "android.intent.extra.TITLE";
+ static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
+ static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
+
/** All system services */
SystemServiceManager mSystemServiceManager;
@@ -1480,8 +1489,9 @@
public ActivityTaskManagerService mActivityTaskManager;
@VisibleForTesting
public ActivityTaskManagerInternal mAtmInternal;
+ UriGrantsManagerInternal mUgmInternal;
@VisibleForTesting
- public UriGrantsManagerInternal mUgmInternal;
+ public final ActivityManagerInternal mInternal;
final ActivityThread mSystemThread;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -2413,6 +2423,8 @@
mProcStartHandler = null;
mHiddenApiBlacklist = null;
mFactoryTest = FACTORY_TEST_OFF;
+ mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
+ mInternal = new LocalService();
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2566,6 +2578,7 @@
Slog.w(TAG, "Setting background thread cpuset failed");
}
+ mInternal = new LocalService();
}
public void setSystemServiceManager(SystemServiceManager mgr) {
@@ -2583,7 +2596,7 @@
mBatteryStatsService.publish();
mAppOpsService.publish(mContext);
Slog.d("AppOps", "AppOpsService published");
- LocalServices.addService(ActivityManagerInternal.class, new LocalService());
+ LocalServices.addService(ActivityManagerInternal.class, mInternal);
mActivityTaskManager.onActivityManagerInternalAdded();
mUgmInternal.onActivityManagerInternalAdded();
mPendingIntentController.onActivityManagerInternalAdded();
@@ -8197,6 +8210,53 @@
@Deprecated
@Override
public void requestBugReport(int bugreportType) {
+ requestBugReportWithDescription(null, null, bugreportType);
+ }
+
+ /**
+ * @deprecated This method is only used by a few internal components and it will soon be
+ * replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
+ * No new code should be calling it.
+ */
+ @Deprecated
+ public void requestBugReportWithDescription(@Nullable String shareTitle,
+ @Nullable String shareDescription, int bugreportType) {
+ if (!TextUtils.isEmpty(shareTitle)) {
+ if (shareTitle.length() > MAX_BUGREPORT_TITLE_SIZE) {
+ String errorStr = "shareTitle should be less than " +
+ MAX_BUGREPORT_TITLE_SIZE + " characters";
+ throw new IllegalArgumentException(errorStr);
+ }
+ if (!TextUtils.isEmpty(shareDescription)) {
+ int length = shareDescription.getBytes(StandardCharsets.UTF_8).length;
+ if (length > SystemProperties.PROP_VALUE_MAX) {
+ String errorStr = "shareTitle should be less than " +
+ SystemProperties.PROP_VALUE_MAX + " bytes";
+ throw new IllegalArgumentException(errorStr);
+ } else {
+ SystemProperties.set("dumpstate.options.description", shareDescription);
+ }
+ }
+ SystemProperties.set("dumpstate.options.title", shareTitle);
+ Slog.d(TAG, "Bugreport notification title " + shareTitle
+ + " description " + shareDescription);
+ }
+ final boolean useApi = FeatureFlagUtils.isEnabled(mContext,
+ FeatureFlagUtils.USE_BUGREPORT_API);
+
+ if (useApi) {
+ // Create intent to trigger Bugreport API via Shell
+ Intent triggerShellBugreport = new Intent();
+ triggerShellBugreport.setAction(INTENT_BUGREPORT_REQUESTED);
+ triggerShellBugreport.setPackage(SHELL_APP_PACKAGE);
+ triggerShellBugreport.putExtra(EXTRA_BUGREPORT_TYPE, bugreportType);
+ if (shareTitle != null) {
+ triggerShellBugreport.putExtra(EXTRA_TITLE, shareTitle);
+ }
+ if (shareDescription != null) {
+ triggerShellBugreport.putExtra(EXTRA_DESCRIPTION, shareDescription);
+ }
+ }
String extraOptions = null;
switch (bugreportType) {
case ActivityManager.BUGREPORT_OPTION_FULL:
@@ -8238,45 +8298,6 @@
* No new code should be calling it.
*/
@Deprecated
- private void requestBugReportWithDescription(String shareTitle, String shareDescription,
- int bugreportType) {
- if (!TextUtils.isEmpty(shareTitle)) {
- if (shareTitle.length() > MAX_BUGREPORT_TITLE_SIZE) {
- String errorStr = "shareTitle should be less than " +
- MAX_BUGREPORT_TITLE_SIZE + " characters";
- throw new IllegalArgumentException(errorStr);
- } else {
- if (!TextUtils.isEmpty(shareDescription)) {
- int length;
- try {
- length = shareDescription.getBytes("UTF-8").length;
- } catch (UnsupportedEncodingException e) {
- String errorStr = "shareDescription: UnsupportedEncodingException";
- throw new IllegalArgumentException(errorStr);
- }
- if (length > SystemProperties.PROP_VALUE_MAX) {
- String errorStr = "shareTitle should be less than " +
- SystemProperties.PROP_VALUE_MAX + " bytes";
- throw new IllegalArgumentException(errorStr);
- } else {
- SystemProperties.set("dumpstate.options.description", shareDescription);
- }
- }
- SystemProperties.set("dumpstate.options.title", shareTitle);
- }
- }
-
- Slog.d(TAG, "Bugreport notification title " + shareTitle
- + " description " + shareDescription);
- requestBugReport(bugreportType);
- }
-
- /**
- * @deprecated This method is only used by a few internal components and it will soon be
- * replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
- * No new code should be calling it.
- */
- @Deprecated
@Override
public void requestTelephonyBugReport(String shareTitle, String shareDescription) {
requestBugReportWithDescription(shareTitle, shareDescription,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index aa548f2..e90db38 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -621,6 +621,8 @@
mConditionProviders.readXml(
parser, mAllowedManagedServicePackages, forRestore, userId);
migratedManagedServices = true;
+ } else if (mSnoozeHelper.XML_TAG_NAME.equals(parser.getName())) {
+ mSnoozeHelper.readXml(parser);
}
if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) {
if (forRestore && userId != UserHandle.USER_SYSTEM) {
@@ -709,6 +711,7 @@
mPreferencesHelper.writeXml(out, forBackup, userId);
mListeners.writeXml(out, forBackup, userId);
mAssistants.writeXml(out, forBackup, userId);
+ mSnoozeHelper.writeXml(out);
mConditionProviders.writeXml(out, forBackup, userId);
if (!forBackup || userId == UserHandle.USER_SYSTEM) {
writeSecureNotificationsPolicy(out);
@@ -1753,6 +1756,7 @@
com.android.internal.R.integer.config_notificationWarnRemoteViewSizeBytes);
mStripRemoteViewsSizeBytes = getContext().getResources().getInteger(
com.android.internal.R.integer.config_notificationStripRemoteViewSizeBytes);
+
}
@Override
@@ -5284,7 +5288,7 @@
updateLightsLocked();
if (mSnoozeCriterionId != null) {
mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
- mSnoozeHelper.snooze(r);
+ mSnoozeHelper.snooze(r, mSnoozeCriterionId);
} else {
mSnoozeHelper.snooze(r, mDuration);
}
@@ -5387,6 +5391,27 @@
@Override
public void run() {
synchronized (mNotificationLock) {
+ final Long snoozeAt =
+ mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ r.getUser().getIdentifier(),
+ r.sbn.getPackageName(), r.sbn.getKey());
+ final long currentTime = System.currentTimeMillis();
+ if (snoozeAt.longValue() > currentTime) {
+ (new SnoozeNotificationRunnable(r.sbn.getKey(),
+ snoozeAt.longValue() - currentTime, null)).snoozeLocked(r);
+ return;
+ }
+
+ final String contextId =
+ mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+ r.getUser().getIdentifier(),
+ r.sbn.getPackageName(), r.sbn.getKey());
+ if (contextId != null) {
+ (new SnoozeNotificationRunnable(r.sbn.getKey(),
+ 0, contextId)).snoozeLocked(r);
+ return;
+ }
+
mEnqueuedNotifications.add(r);
scheduleTimeoutLocked(r);
@@ -6937,6 +6962,7 @@
if (DBG) {
Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
}
+ mSnoozeHelper.cleanupPersistedContext(key);
mSnoozeHelper.repost(key);
handleSavePolicyFile();
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 91f497c..8125d0d 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -55,6 +55,21 @@
* NotificationManagerService helper for handling snoozed notifications.
*/
public class SnoozeHelper {
+ public static final String XML_SNOOZED_NOTIFICATION_VERSION = "1";
+
+ protected static final String XML_TAG_NAME = "snoozed-notifications";
+
+ private static final String XML_SNOOZED_NOTIFICATION = "notification";
+ private static final String XML_SNOOZED_NOTIFICATION_CONTEXT = "context";
+ private static final String XML_SNOOZED_NOTIFICATION_PKG = "pkg";
+ private static final String XML_SNOOZED_NOTIFICATION_USER_ID = "user-id";
+ private static final String XML_SNOOZED_NOTIFICATION_KEY = "key";
+ //the time the snoozed notification should be reposted
+ private static final String XML_SNOOZED_NOTIFICATION_TIME = "time";
+ private static final String XML_SNOOZED_NOTIFICATION_CONTEXT_ID = "id";
+ private static final String XML_SNOOZED_NOTIFICATION_VERSION_LABEL = "version";
+
+
private static final String TAG = "SnoozeHelper";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String INDENT = " ";
@@ -72,6 +87,17 @@
// User id : package name : notification key : record.
private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>>
mSnoozedNotifications = new ArrayMap<>();
+ // User id : package name : notification key : time-milliseconds .
+ // This member stores persisted snoozed notification trigger times. it persists through reboots
+ // It should have the notifications that haven't expired or re-posted yet
+ private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, Long>>>
+ mPersistedSnoozedNotifications = new ArrayMap<>();
+ // User id : package name : notification key : creation ID .
+ // This member stores persisted snoozed notification trigger context for the assistant
+ // it persists through reboots.
+ // It should have the notifications that haven't expired or re-posted yet
+ private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, String>>>
+ mPersistedSnoozedNotificationsWithContext = new ArrayMap<>();
// notification key : package.
private ArrayMap<String, String> mPackages = new ArrayMap<>();
// key : userId
@@ -89,6 +115,34 @@
mUserProfiles = userProfiles;
}
+ void cleanupPersistedContext(String key){
+ int userId = mUsers.get(key);
+ String pkg = mPackages.get(key);
+ synchronized (mPersistedSnoozedNotificationsWithContext) {
+ removeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+ }
+ }
+
+ //This function has a side effect of removing the time from the list of persisted notifications.
+ //IT IS NOT IDEMPOTENT!
+ @NonNull
+ protected Long getSnoozeTimeForUnpostedNotification(int userId, String pkg, String key) {
+ Long time;
+ synchronized (mPersistedSnoozedNotifications) {
+ time = removeRecord(pkg, key, userId, mPersistedSnoozedNotifications);
+ }
+ if (time == null) {
+ return 0L;
+ }
+ return time;
+ }
+
+ protected String getSnoozeContextForUnpostedNotification(int userId, String pkg, String key) {
+ synchronized (mPersistedSnoozedNotificationsWithContext) {
+ return removeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+ }
+ }
+
protected boolean isSnoozed(int userId, String pkg, String key) {
return mSnoozedNotifications.containsKey(userId)
&& mSnoozedNotifications.get(userId).containsKey(pkg)
@@ -169,32 +223,82 @@
* Snoozes a notification and schedules an alarm to repost at that time.
*/
protected void snooze(NotificationRecord record, long duration) {
+ String pkg = record.sbn.getPackageName();
+ String key = record.getKey();
+ int userId = record.getUser().getIdentifier();
+
snooze(record);
- scheduleRepost(record.sbn.getPackageName(), record.getKey(), record.getUserId(), duration);
+ scheduleRepost(pkg, key, userId, duration);
+ Long activateAt = System.currentTimeMillis() + duration;
+ synchronized (mPersistedSnoozedNotifications) {
+ storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, activateAt);
+ }
}
/**
* Records a snoozed notification.
*/
- protected void snooze(NotificationRecord record) {
+ protected void snooze(NotificationRecord record, String contextId) {
+ int userId = record.getUser().getIdentifier();
+ if (contextId != null) {
+ synchronized (mPersistedSnoozedNotificationsWithContext) {
+ storeRecord(record.sbn.getPackageName(), record.getKey(),
+ userId, mPersistedSnoozedNotificationsWithContext, contextId);
+ }
+ }
+ snooze(record);
+ }
+
+ private void snooze(NotificationRecord record) {
int userId = record.getUser().getIdentifier();
if (DEBUG) {
Slog.d(TAG, "Snoozing " + record.getKey());
}
- ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
- mSnoozedNotifications.get(userId);
+ storeRecord(record.sbn.getPackageName(), record.getKey(),
+ userId, mSnoozedNotifications, record);
+ mPackages.put(record.getKey(), record.sbn.getPackageName());
+ mUsers.put(record.getKey(), userId);
+ }
+
+ private <T> void storeRecord(String pkg, String key, Integer userId,
+ ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, T object) {
+
+ ArrayMap<String, ArrayMap<String, T>> records =
+ targets.get(userId);
if (records == null) {
records = new ArrayMap<>();
}
- ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.sbn.getPackageName());
+ ArrayMap<String, T> pkgRecords = records.get(pkg);
if (pkgRecords == null) {
pkgRecords = new ArrayMap<>();
}
- pkgRecords.put(record.getKey(), record);
- records.put(record.sbn.getPackageName(), pkgRecords);
- mSnoozedNotifications.put(userId, records);
- mPackages.put(record.getKey(), record.sbn.getPackageName());
- mUsers.put(record.getKey(), userId);
+ pkgRecords.put(key, object);
+ records.put(pkg, pkgRecords);
+ targets.put(userId, records);
+
+ }
+
+ private <T> T removeRecord(String pkg, String key, Integer userId,
+ ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets) {
+ T object = null;
+
+ ArrayMap<String, ArrayMap<String, T>> records =
+ targets.get(userId);
+ if (records == null) {
+ return null;
+ }
+ ArrayMap<String, T> pkgRecords = records.get(pkg);
+ if (pkgRecords == null) {
+ return null;
+ }
+ object = pkgRecords.remove(key);
+ if (pkgRecords.size() == 0) {
+ records.remove(pkg);
+ }
+ if (records.size() == 0) {
+ targets.remove(userId);
+ }
+ return object;
}
protected boolean cancel(int userId, String pkg, String tag, int id) {
@@ -414,13 +518,121 @@
}
}
- protected void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
-
+ protected void writeXml(XmlSerializer out) throws IOException {
+ final long currentTime = System.currentTimeMillis();
+ out.startTag(null, XML_TAG_NAME);
+ writeXml(out, mPersistedSnoozedNotifications, XML_SNOOZED_NOTIFICATION,
+ value -> {
+ if (value < currentTime) {
+ return;
+ }
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_TIME,
+ value.toString());
+ });
+ writeXml(out, mPersistedSnoozedNotificationsWithContext, XML_SNOOZED_NOTIFICATION_CONTEXT,
+ value -> {
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID,
+ value);
+ });
+ out.endTag(null, XML_TAG_NAME);
}
- public void readXml(XmlPullParser parser, boolean forRestore)
- throws XmlPullParserException, IOException {
+ private interface Inserter<T> {
+ void insert(T t) throws IOException;
+ }
+ private <T> void writeXml(XmlSerializer out,
+ ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, String tag,
+ Inserter<T> attributeInserter)
+ throws IOException {
+ synchronized (targets) {
+ final int M = targets.size();
+ for (int i = 0; i < M; i++) {
+ final ArrayMap<String, ArrayMap<String, T>> packages =
+ targets.valueAt(i);
+ if (packages == null) {
+ continue;
+ }
+ final int N = packages.size();
+ for (int j = 0; j < N; j++) {
+ final ArrayMap<String, T> keyToValue = packages.valueAt(j);
+ if (keyToValue == null) {
+ continue;
+ }
+ final int O = keyToValue.size();
+ for (int k = 0; k < O; k++) {
+ T value = keyToValue.valueAt(k);
+ out.startTag(null, tag);
+
+ attributeInserter.insert(value);
+
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
+ XML_SNOOZED_NOTIFICATION_VERSION);
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, keyToValue.keyAt(k));
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, packages.keyAt(j));
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
+ targets.keyAt(i).toString());
+
+ out.endTag(null, tag);
+
+ }
+ }
+ }
+ }
+ }
+
+ protected void readXml(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ String tag = parser.getName();
+ if (type == XmlPullParser.END_TAG
+ && XML_TAG_NAME.equals(tag)) {
+ break;
+ }
+ if (type == XmlPullParser.START_TAG
+ && (XML_SNOOZED_NOTIFICATION.equals(tag)
+ || tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT))
+ && parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL)
+ .equals(XML_SNOOZED_NOTIFICATION_VERSION)) {
+ try {
+ final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY);
+ final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG);
+ final int userId = Integer.parseInt(
+ parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_USER_ID));
+ if (tag.equals(XML_SNOOZED_NOTIFICATION)) {
+ final Long time = Long.parseLong(
+ parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_TIME));
+ if (time > System.currentTimeMillis()) { //only read new stuff
+ synchronized (mPersistedSnoozedNotifications) {
+ storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, time);
+ }
+ scheduleRepost(pkg, key, userId, time - System.currentTimeMillis());
+ }
+ continue;
+ }
+ if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) {
+ final String creationId = parser.getAttributeValue(
+ null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID);
+ synchronized (mPersistedSnoozedNotificationsWithContext) {
+ storeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext,
+ creationId);
+ }
+ continue;
+ }
+
+
+ } catch (Exception e) {
+ //we dont cre if it is a number format exception or a null pointer exception.
+ //we just want to debug it and continue with our lives
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Exception in reading snooze data from policy xml: "
+ + e.getMessage());
+ }
+ }
+ }
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0032e9a..3aeb2b1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1184,9 +1184,26 @@
synchronized (mSessions) {
pw.println("Active install sessions:");
pw.increaseIndent();
+
+ List<PackageInstallerSession> finalizedSessions = new ArrayList<>();
int N = mSessions.size();
for (int i = 0; i < N; i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
+ if (session.isStagedAndInTerminalState()) {
+ finalizedSessions.add(session);
+ continue;
+ }
+ session.dump(pw);
+ pw.println();
+ }
+ pw.println();
+ pw.decreaseIndent();
+
+ pw.println("Finalized install sessions:");
+ pw.increaseIndent();
+ N = finalizedSessions.size();
+ for (int i = 0; i < N; i++) {
+ final PackageInstallerSession session = finalizedSessions.get(i);
session.dump(pw);
pw.println();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4eddb930..b720290 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2337,6 +2337,7 @@
pw.printPair("mInstallerPackageName", mInstallerPackageName);
pw.printPair("mInstallerUid", mInstallerUid);
pw.printPair("createdMillis", createdMillis);
+ pw.printPair("updatedMillis", updatedMillis);
pw.printPair("stageDir", stageDir);
pw.printPair("stageCid", stageCid);
pw.println();
@@ -2356,6 +2357,13 @@
pw.printPair("mFinalMessage", mFinalMessage);
pw.printPair("params.isMultiPackage", params.isMultiPackage);
pw.printPair("params.isStaged", params.isStaged);
+ pw.printPair("mParentSessionId", mParentSessionId);
+ pw.printPair("mChildSessionIds", mChildSessionIds);
+ pw.printPair("mStagedSessionApplied", mStagedSessionApplied);
+ pw.printPair("mStagedSessionFailed", mStagedSessionFailed);
+ pw.printPair("mStagedSessionReady", mStagedSessionReady);
+ pw.printPair("mStagedSessionErrorCode", mStagedSessionErrorCode);
+ pw.printPair("mStagedSessionErrorMessage", mStagedSessionErrorMessage);
pw.println();
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 02f98b4..bfd280c 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -271,33 +271,9 @@
}, filter, null, getHandler());
}
- /**
- * This method posts a blocking call to the handler thread, so it should not be called from
- * that same thread.
- * @throws {@link IllegalStateException} if called from {@link #mHandlerThread}
- */
@Override
public ParceledListSlice getAvailableRollbacks() {
enforceManageRollbacks("getAvailableRollbacks");
- if (Thread.currentThread().equals(mHandlerThread)) {
- Slog.wtf(TAG, "Calling getAvailableRollbacks from mHandlerThread "
- + "causes a deadlock");
- throw new IllegalStateException("Cannot call RollbackManager#getAvailableRollbacks "
- + "from the handler thread!");
- }
-
- // Wait for the handler thread to get the list of available rollbacks
- // to get the most up-to-date results. This is intended to reduce test
- // flakiness when checking available rollbacks immediately after
- // installing a package with rollback enabled.
- CountDownLatch latch = new CountDownLatch(1);
- getHandler().post(() -> latch.countDown());
- try {
- latch.await();
- } catch (InterruptedException ie) {
- throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
- }
-
synchronized (mLock) {
List<RollbackInfo> rollbacks = new ArrayList<>();
for (int i = 0; i < mRollbacks.size(); ++i) {
@@ -306,6 +282,15 @@
rollbacks.add(rollback.info);
}
}
+
+ // Also return new rollbacks for which the PackageRollbackInfo is complete.
+ for (NewRollback newRollback : mNewRollbacks) {
+ if (newRollback.rollback.info.getPackages().size()
+ == newRollback.packageSessionIds.length
+ && !newRollback.isCancelled) {
+ rollbacks.add(newRollback.rollback.info);
+ }
+ }
return new ParceledListSlice<>(rollbacks);
}
}
@@ -562,6 +547,14 @@
}
}
}
+ for (NewRollback newRollback : mNewRollbacks) {
+ for (PackageRollbackInfo info : newRollback.rollback.info.getPackages()) {
+ if (info.getPackageName().equals(packageName)) {
+ newRollback.isCancelled = true;
+ break;
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index f1cd721..5a0dfd0 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -41,6 +41,7 @@
import static com.android.server.am.ActivityDisplayProto.SINGLE_TASK_INSTANCE;
import static com.android.server.am.ActivityDisplayProto.STACKS;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
@@ -173,18 +174,10 @@
mService = root.mService;
mDisplayId = display.getDisplayId();
mDisplay = display;
- mDisplayContent = createDisplayContent();
+ mDisplayContent = mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
mDisplayContent.reconfigureDisplayLocked();
- updateBounds();
- }
-
- protected DisplayContent createDisplayContent() {
- return mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
- }
-
- private void updateBounds() {
- mDisplay.getRealSize(mTmpDisplaySize);
- setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+ onRequestedOverrideConfigurationChanged(
+ mDisplayContent.getRequestedOverrideConfiguration());
}
void onDisplayChanged() {
@@ -200,7 +193,8 @@
}
}
- updateBounds();
+ mDisplay.getRealSize(mTmpDisplaySize);
+ setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
if (mDisplayContent != null) {
mDisplayContent.updateDisplayInfo();
mService.mWindowManager.requestTraversal();
@@ -1541,6 +1535,17 @@
return mSingleTaskInstance;
}
+ @VisibleForTesting
+ void removeAllTasks() {
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getChildAt(i);
+ final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ for (int j = tasks.size() - 1; j >= 0; --j) {
+ stack.removeTask(tasks.get(j), "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
+ }
+ }
+ }
+
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size()
+ (mSingleTaskInstance ? " mSingleTaskInstance" : ""));
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 31e8bbdab..2269537 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -121,6 +121,7 @@
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
@@ -1372,16 +1373,17 @@
return stack != null ? stack.getDisplay() : null;
}
- boolean changeWindowTranslucency(boolean toOpaque) {
- if (fullscreen == toOpaque) {
- return false;
+ boolean setOccludesParent(boolean occludesParent) {
+ final boolean changed = mAppWindowToken.setOccludesParent(occludesParent);
+ if (changed) {
+ if (!occludesParent) {
+ getActivityStack().convertActivityToTranslucent(this);
+ }
+ // Keep track of the number of fullscreen activities in this task.
+ task.numFullscreen += occludesParent ? +1 : -1;
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
-
- // Keep track of the number of fullscreen activities in this task.
- task.numFullscreen += toOpaque ? +1 : -1;
-
- fullscreen = toOpaque;
- return true;
+ return changed;
}
void takeFromHistory() {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 12eab50..daf3286 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2492,7 +2492,7 @@
mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
if (waitingActivity != null) {
- mWindowManager.setWindowOpaque(waitingActivity.appToken, false);
+ mWindowManager.setWindowOpaqueLocked(waitingActivity.appToken, false);
if (waitingActivity.attachedToProcess()) {
try {
waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
@@ -2813,7 +2813,7 @@
// Launching this app's activity, make sure the app is no longer
// considered stopped.
try {
- AppGlobals.getPackageManager().setPackageStoppedState(
+ mService.getPackageManager().setPackageStoppedState(
next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
} catch (RemoteException e1) {
} catch (IllegalArgumentException e) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 1c56a10..22f72a4 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -250,7 +250,7 @@
RecentTasks mRecentTasks;
/** Helper class to abstract out logic for fetching the set of currently running tasks */
- RunningTasks mRunningTasks;
+ private RunningTasks mRunningTasks;
final ActivityStackSupervisorHandler mHandler;
final Looper mLooper;
@@ -444,7 +444,7 @@
}
mInitialized = true;
- mRunningTasks = createRunningTasks();
+ setRunningTasks(new RunningTasks());
mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
mHandler.getLooper());
@@ -485,13 +485,20 @@
}
void setRecentTasks(RecentTasks recentTasks) {
+ if (mRecentTasks != null) {
+ mRecentTasks.unregisterCallback(this);
+ }
mRecentTasks = recentTasks;
mRecentTasks.registerCallback(this);
}
@VisibleForTesting
- RunningTasks createRunningTasks() {
- return new RunningTasks();
+ void setRunningTasks(RunningTasks runningTasks) {
+ mRunningTasks = runningTasks;
+ }
+
+ RunningTasks getRunningTasks() {
+ return mRunningTasks;
}
/**
@@ -2735,7 +2742,7 @@
mWindowManager.deferSurfaceLayout();
try {
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mWindowManager.setDockedStackCreateState(
+ mWindowManager.setDockedStackCreateStateLocked(
activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);
// Defer updating the stack in which recents is until the app transition is done, to
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a3ab27e..7283ca5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -43,7 +43,6 @@
import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
-import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
@@ -361,7 +360,7 @@
/* Global service lock used by the package the owns this service. */
final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
/**
- * It is the same instance as {@link mGlobalLock}, just declared as a type that the
+ * It is the same instance as {@link #mGlobalLock}, just declared as a type that the
* locked-region-code-injection does't recognize it. It is used to skip wrapping priority
* booster for places that are already in the scope of another booster (e.g. computing oom-adj).
*
@@ -730,7 +729,6 @@
final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
final boolean forceResizable = Settings.Global.getInt(
resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
- final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
// Transfer any global setting for forcing RTL layout, into a System Property
DisplayProperties.debug_force_rtl(forceRtl);
@@ -761,10 +759,6 @@
mSupportsPictureInPicture = false;
mSupportsMultiDisplay = false;
}
- mWindowManager.setForceResizableTasks(mForceResizableActivities);
- mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
- mWindowManager.setSupportsFreeformWindowManagement(mSupportsFreeformWindowManagement);
- mWindowManager.setIsPc(isPc);
mWindowManager.mRoot.onSettingsRetrieved();
// This happens before any activities are started, so we can change global configuration
// in-place.
@@ -821,8 +815,7 @@
new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mH);
mActivityStartController = new ActivityStartController(this);
- mRecentTasks = createRecentTasks();
- mStackSupervisor.setRecentTasks(mRecentTasks);
+ setRecentTasks(new RecentTasks(this, mStackSupervisor));
mVrController = new VrController(mGlobalLock);
mKeyguardController = mStackSupervisor.getKeyguardController();
}
@@ -890,8 +883,10 @@
return mode == AppOpsManager.MODE_ALLOWED;
}
- protected RecentTasks createRecentTasks() {
- return new RecentTasks(this, mStackSupervisor);
+ @VisibleForTesting
+ protected void setRecentTasks(RecentTasks recentTasks) {
+ mRecentTasks = recentTasks;
+ mStackSupervisor.setRecentTasks(recentTasks);
}
RecentTasks getRecentTasks() {
@@ -1954,12 +1949,7 @@
if (r == null) {
return false;
}
- final boolean translucentChanged = r.changeWindowTranslucency(true);
- if (translucentChanged) {
- mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- }
- mWindowManager.setAppFullscreen(token, true);
- return translucentChanged;
+ return r.setOccludesParent(true);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -1982,13 +1972,7 @@
ActivityRecord under = task.mActivities.get(index - 1);
under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
}
- final boolean translucentChanged = r.changeWindowTranslucency(false);
- if (translucentChanged) {
- r.getActivityStack().convertActivityToTranslucent(r);
- }
- mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- mWindowManager.setAppFullscreen(token, false);
- return translucentChanged;
+ return r.setOccludesParent(false);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2581,7 +2565,7 @@
+ taskId + " to stack " + stackId);
}
if (stack.inSplitScreenPrimaryWindowingMode()) {
- mWindowManager.setDockedStackCreateState(
+ mWindowManager.setDockedStackCreateStateLocked(
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
}
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
@@ -2700,7 +2684,7 @@
+ " non-standard task " + taskId + " to split-screen windowing mode");
}
- mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ mWindowManager.setDockedStackCreateStateLocked(createMode, initialBounds);
final int windowingMode = task.getWindowingMode();
final ActivityStack stack = task.getStack();
if (toTop) {
@@ -2802,7 +2786,7 @@
try {
synchronized (mGlobalLock) {
// Cancel the recents animation synchronously (do not hold the WM lock)
- mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
+ mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition
? REORDER_MOVE_TO_ORIGINAL_POSITION
: REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 7fde6de..7e0d9a0 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -92,6 +92,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
@@ -153,8 +154,11 @@
final ComponentName mActivityComponent;
final boolean mVoiceInteraction;
- /** @see WindowContainer#fillsParent() */
- private boolean mFillsParent;
+ /**
+ * The activity is opaque and fills the entire space of this task.
+ * @see WindowContainer#fillsParent()
+ */
+ private boolean mOccludesParent;
boolean mShowForAllUsers;
int mTargetSdk;
@@ -373,7 +377,7 @@
appToken = token;
mActivityComponent = activityComponent;
mVoiceInteraction = voiceInteraction;
- mFillsParent = fillsParent;
+ mOccludesParent = fillsParent;
mInputApplicationHandle = new InputApplicationHandle(appToken.asBinder());
}
@@ -2354,11 +2358,29 @@
@Override
boolean fillsParent() {
- return mFillsParent;
+ return occludesParent();
}
- void setFillsParent(boolean fillsParent) {
- mFillsParent = fillsParent;
+ /** Returns true if this activity is opaque and fills the entire space of this task. */
+ boolean occludesParent() {
+ return mOccludesParent;
+ }
+
+ boolean setOccludesParent(boolean occludesParent) {
+ final boolean changed = occludesParent != mOccludesParent;
+ mOccludesParent = occludesParent;
+ setMainWindowOpaque(occludesParent);
+ mWmService.mWindowPlacerLocked.requestTraversal();
+ return changed;
+ }
+
+ void setMainWindowOpaque(boolean isOpaque) {
+ final WindowState win = findMainWindow();
+ if (win == null) {
+ return;
+ }
+ isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
+ win.mWinAnimator.setOpaqueLocked(isOpaque);
}
boolean containsDismissKeyguardWindow() {
@@ -3035,7 +3057,7 @@
}
pw.println(prefix + "component=" + mActivityComponent.flattenToShortString());
pw.print(prefix); pw.print("task="); pw.println(getTask());
- pw.print(prefix); pw.print(" mFillsParent="); pw.print(mFillsParent);
+ pw.print(prefix); pw.print(" mOccludesParent="); pw.print(mOccludesParent);
pw.print(" mOrientation="); pw.println(mOrientation);
pw.println(prefix + "hiddenRequested=" + hiddenRequested + " mClientHidden=" + mClientHidden
+ ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
@@ -3152,7 +3174,7 @@
if (mThumbnail != null){
mThumbnail.writeToProto(proto, THUMBNAIL);
}
- proto.write(FILLS_PARENT, mFillsParent);
+ proto.write(FILLS_PARENT, mOccludesParent);
proto.write(APP_STOPPED, mAppStopped);
proto.write(HIDDEN_REQUESTED, hiddenRequested);
proto.write(CLIENT_HIDDEN, mClientHidden);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 282ed42..410cc94 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -295,7 +295,8 @@
false /* forceRelayout */);
}
- private void setUserRotation(int userRotationMode, int userRotation) {
+ @VisibleForTesting
+ void setUserRotation(int userRotationMode, int userRotation) {
if (isDefaultDisplay) {
// We'll be notified via settings listener, so we don't need to update internal values.
final ContentResolver res = mContext.getContentResolver();
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 207e8ef..8507918 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -275,14 +275,14 @@
// This display used to be in freeform, but we don't support freeform anymore, so fall
// back to fullscreen.
if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
- && !mService.mSupportsFreeformWindowManagement) {
+ && !mService.mAtmService.mSupportsFreeformWindowManagement) {
return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
}
// No record is present so use default windowing mode policy.
if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays
&& displayId != Display.DEFAULT_DISPLAY;
- windowingMode = mService.mSupportsFreeformWindowManagement
+ windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement
&& (mService.mIsPc || forceDesktopMode)
? WindowConfiguration.WINDOWING_MODE_FREEFORM
: WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index b1bc2197..120ce3e 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -355,7 +355,9 @@
void getTouchRegion(Rect outRegion) {
outRegion.set(mTouchRegion);
- outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
+ if (mWindow != null) {
+ outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
+ }
}
private void resetDragResizingChangeReported() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 4f0332c..caa8363 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -241,7 +241,7 @@
// Fetch all the surface controls and pass them to the client to get the animation
// started. Cancel any existing recents animation running synchronously (do not hold the
// WM lock)
- mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION,
+ mWindowManager.cancelRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION,
"startRecentsActivity");
mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
this, mDefaultDisplay.mDisplayId,
@@ -396,12 +396,8 @@
@Override
public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode,
- boolean runSychronously, boolean sendUserLeaveHint) {
- if (runSychronously) {
- finishAnimation(reorderMode, sendUserLeaveHint);
- } else {
- mService.mH.post(() -> finishAnimation(reorderMode, sendUserLeaveHint));
- }
+ boolean sendUserLeaveHint) {
+ finishAnimation(reorderMode, sendUserLeaveHint);
}
@Override
@@ -435,8 +431,7 @@
} else {
// Just cancel directly to unleash from launcher when the next launching task is the
// current top task.
- mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE,
- "stackOrderChanged");
+ mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "stackOrderChanged");
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 724a72e..8752f37 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -168,8 +168,7 @@
public interface RecentsAnimationCallbacks {
/** Callback when recents animation is finished. */
- void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously,
- boolean sendUserLeaveHint);
+ void onAnimationFinished(@ReorderMode int reorderMode, boolean sendUserLeaveHint);
}
private final IRecentsAnimationController mController =
@@ -221,8 +220,7 @@
// prior to calling the callback
mCallbacks.onAnimationFinished(moveHomeToTop
? REORDER_MOVE_TO_TOP
- : REORDER_MOVE_TO_ORIGINAL_POSITION,
- true /* runSynchronously */, sendUserLeaveHint);
+ : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint);
mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN);
} finally {
Binder.restoreCallingIdentity(token);
@@ -498,21 +496,15 @@
}
void cancelAnimation(@ReorderMode int reorderMode, String reason) {
- cancelAnimation(reorderMode, false /* runSynchronously */, false /*screenshot */, reason);
- }
-
- void cancelAnimationSynchronously(@ReorderMode int reorderMode, String reason) {
- cancelAnimation(reorderMode, true /* runSynchronously */, false /* screenshot */, reason);
+ cancelAnimation(reorderMode, false /*screenshot */, reason);
}
void cancelAnimationWithScreenshot(boolean screenshot) {
- cancelAnimation(REORDER_KEEP_IN_PLACE, true /* sync */, screenshot, "stackOrderChanged");
+ cancelAnimation(REORDER_KEEP_IN_PLACE, screenshot, "stackOrderChanged");
}
- private void cancelAnimation(@ReorderMode int reorderMode, boolean runSynchronously,
- boolean screenshot, String reason) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason
- + " runSynchronously=" + runSynchronously);
+ private void cancelAnimation(@ReorderMode int reorderMode, boolean screenshot, String reason) {
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
synchronized (mService.getWindowManagerLock()) {
if (mCanceled) {
// We've already canceled the animation
@@ -525,16 +517,14 @@
// Screen shot previous task when next task starts transition and notify the runner.
// We will actually finish the animation once the runner calls cleanUpScreenshot().
final Task task = mPendingAnimations.get(0).mTask;
- final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode,
- runSynchronously);
+ final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode);
try {
mRunner.onAnimationCanceled(taskSnapshot);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
if (taskSnapshot == null) {
- mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
- false /* sendUserLeaveHint */);
+ mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
}
} else {
// Otherwise, notify the runner and clean up the animation immediately
@@ -545,8 +535,7 @@
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
- mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
- false /* sendUserLeaveHint */);
+ mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
}
}
}
@@ -592,8 +581,7 @@
return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
}
- TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode,
- boolean runSynchronously) {
+ TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode) {
final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
final ArraySet<Task> tasks = Sets.newArraySet(task);
snapshotController.snapshotTasks(tasks);
@@ -613,8 +601,7 @@
if (DEBUG_RECENTS_ANIMATIONS) {
Slog.d(TAG, "mRecentScreenshotAnimator finish");
}
- mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
- false /* sendUserLeaveHint */);
+ mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
}, mService);
mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
return taskSnapshot;
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 66d42db..3401de6 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -227,15 +227,10 @@
mStackSupervisor.mRootActivityContainer = this;
}
- @VisibleForTesting
- void setWindowContainer(RootWindowContainer container) {
- mRootWindowContainer = container;
- mRootWindowContainer.setRootActivityContainer(this);
- }
-
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
- setWindowContainer(mWindowManager.mRoot);
+ mRootWindowContainer = mWindowManager.mRoot;
+ mRootWindowContainer.setRootActivityContainer(this);
mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -2266,7 +2261,7 @@
@WindowConfiguration.ActivityType int ignoreActivityType,
@WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
boolean allowed) {
- mStackSupervisor.mRunningTasks.getTasks(maxNum, list, ignoreActivityType,
+ mStackSupervisor.getRunningTasks().getTasks(maxNum, list, ignoreActivityType,
ignoreWindowingMode, mActivityDisplays, callingUid, allowed);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fd86faa..3a2eb57 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -380,7 +380,7 @@
boolean isResizeable() {
return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
- || mWmService.mForceResizableTasks;
+ || mWmService.mAtmService.mForceResizableActivities;
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 79367a0..cc2112e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1666,7 +1666,7 @@
* default bounds.
*/
Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
- if (!mWmService.mSupportsPictureInPicture) {
+ if (!mWmService.mAtmService.mSupportsPictureInPicture) {
return null;
}
@@ -1762,7 +1762,7 @@
* Sets the current picture-in-picture aspect ratio.
*/
void setPictureInPictureAspectRatio(float aspectRatio) {
- if (!mWmService.mSupportsPictureInPicture) {
+ if (!mWmService.mAtmService.mSupportsPictureInPicture) {
return;
}
@@ -1792,7 +1792,7 @@
* Sets the current picture-in-picture actions.
*/
void setPictureInPictureActions(List<RemoteAction> actions) {
- if (!mWmService.mSupportsPictureInPicture) {
+ if (!mWmService.mAtmService.mSupportsPictureInPicture) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bbef261..29d232f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -767,11 +767,11 @@
*/
void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
@Nullable ConfigurationContainer requestingContainer) {
- final boolean changed = mOrientation != orientation;
- mOrientation = orientation;
- if (!changed) {
+ if (mOrientation == orientation) {
return;
}
+
+ mOrientation = orientation;
final WindowContainer parent = getParent();
if (parent != null) {
onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 86faad0..d8d6841 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -28,6 +28,7 @@
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
+import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
@@ -591,9 +592,6 @@
int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
Rect mDockedStackCreateBounds;
- boolean mForceResizableTasks;
- boolean mSupportsPictureInPicture;
- boolean mSupportsFreeformWindowManagement;
boolean mIsPc;
/**
* Flag that indicates that desktop mode is forced for public secondary screens.
@@ -819,7 +817,7 @@
int mTransactionSequence;
final WindowAnimator mAnimator;
- final SurfaceAnimationRunner mSurfaceAnimationRunner;
+ SurfaceAnimationRunner mSurfaceAnimationRunner;
/**
* Keeps track of which animations got transferred to which animators. Entries will get cleaned
@@ -957,6 +955,9 @@
final ArrayList<AppFreezeListener> mAppFreezeListeners = new ArrayList<>();
+ @VisibleForTesting
+ final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener;
+
interface AppFreezeListener {
void onAppFreezeTimeout();
}
@@ -1010,6 +1011,7 @@
mGlobalLock = atm.getGlobalLock();
mAtmService = atm;
mContext = context;
+ mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mAllowBootMessages = showBootMsgs;
mOnlyCore = onlyCore;
mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -1159,26 +1161,28 @@
mSystemGestureExcludedByPreQStickyImmersive =
DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- new HandlerExecutor(mH), properties -> {
- synchronized (mGlobalLock) {
- final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
- properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
- final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
- if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
- || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
- mSystemGestureExclusionLimitDp = exclusionLimitDp;
- mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
- mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
- }
- mSystemGestureExclusionLogDebounceTimeoutMillis =
- DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS, 0);
- }
- });
+ mPropertiesChangedListener = properties -> {
+ synchronized (mGlobalLock) {
+ final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
+ properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+ final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
+ if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
+ || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
+ mSystemGestureExclusionLimitDp = exclusionLimitDp;
+ mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
+ mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
+ }
+
+ mSystemGestureExclusionLogDebounceTimeoutMillis =
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS, 0);
+ }
+ };
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ new HandlerExecutor(mH), mPropertiesChangedListener);
LocalServices.addService(WindowManagerInternal.class, new LocalService());
}
@@ -2595,7 +2599,7 @@
mRecentsAnimationController = controller;
}
- public RecentsAnimationController getRecentsAnimationController() {
+ RecentsAnimationController getRecentsAnimationController() {
return mRecentsAnimationController;
}
@@ -2603,74 +2607,37 @@
* @return Whether the next recents animation can continue to start. Called from
* {@link RecentsAnimation#startRecentsActivity}.
*/
- public boolean canStartRecentsAnimation() {
- synchronized (mGlobalLock) {
- // TODO(multi-display): currently only default display support recent activity
- if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
- return false;
- }
- return true;
+ boolean canStartRecentsAnimation() {
+ // TODO(multi-display): currently only default display support recent activity
+ if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
+ return false;
}
+ return true;
}
- /**
- * Cancels any running recents animation. The caller should NOT hold the WM lock while calling
- * this method, as it will call back into AM and may cause a deadlock. Any locking will be done
- * in the animation controller itself.
- */
- public void cancelRecentsAnimationSynchronously(
+ void cancelRecentsAnimation(
@RecentsAnimationController.ReorderMode int reorderMode, String reason) {
if (mRecentsAnimationController != null) {
// This call will call through to cleanupAnimation() below after the animation is
// canceled
- mRecentsAnimationController.cancelAnimationSynchronously(reorderMode, reason);
+ mRecentsAnimationController.cancelAnimation(reorderMode, reason);
}
}
- public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
- synchronized (mGlobalLock) {
- if (mRecentsAnimationController != null) {
- final RecentsAnimationController controller = mRecentsAnimationController;
- mRecentsAnimationController = null;
- controller.cleanupAnimation(reorderMode);
- // TODO(mult-display): currently only default display support recents animation.
- getDefaultDisplayContentLocked().mAppTransition.updateBooster();
- }
+ void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
+ if (mRecentsAnimationController != null) {
+ final RecentsAnimationController controller = mRecentsAnimationController;
+ mRecentsAnimationController = null;
+ controller.cleanupAnimation(reorderMode);
+ // TODO(mult-display): currently only default display support recents animation.
+ getDefaultDisplayContentLocked().mAppTransition.updateBooster();
}
}
- public void setAppFullscreen(IBinder token, boolean toOpaque) {
- synchronized (mGlobalLock) {
- final AppWindowToken atoken = mRoot.getAppWindowToken(token);
- if (atoken != null) {
- atoken.setFillsParent(toOpaque);
- setWindowOpaqueLocked(token, toOpaque);
- mWindowPlacerLocked.requestTraversal();
- }
- }
- }
-
- public void setWindowOpaque(IBinder token, boolean isOpaque) {
- synchronized (mGlobalLock) {
- setWindowOpaqueLocked(token, isOpaque);
- }
- }
-
- private void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
+ void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
if (wtoken != null) {
- final WindowState win = wtoken.findMainWindow();
- if (win == null) {
- return;
- }
- isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
- win.mWinAnimator.setOpaqueLocked(isOpaque);
- }
- }
-
- public void setDockedStackCreateState(int mode, Rect bounds) {
- synchronized (mGlobalLock) {
- setDockedStackCreateStateLocked(mode, bounds);
+ wtoken.setMainWindowOpaque(isOpaque);
}
}
@@ -2679,14 +2646,12 @@
mDockedStackCreateBounds = bounds;
}
- public void checkSplitScreenMinimizedChanged(boolean animate) {
- synchronized (mGlobalLock) {
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- displayContent.getDockedDividerController().checkMinimizeChanged(animate);
- }
+ void checkSplitScreenMinimizedChanged(boolean animate) {
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ displayContent.getDockedDividerController().checkMinimizeChanged(animate);
}
- public boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
+ boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
aspectRatio);
@@ -4847,8 +4812,10 @@
case UPDATE_DOCKED_STACK_DIVIDER: {
synchronized (mGlobalLock) {
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- displayContent.getDockedDividerController().reevaluateVisibility(false);
- displayContent.adjustForImeIfNeeded();
+ if (displayContent != null) {
+ displayContent.getDockedDividerController().reevaluateVisibility(false);
+ displayContent.adjustForImeIfNeeded();
+ }
}
break;
}
@@ -6495,31 +6462,14 @@
}
}
- public void setForceResizableTasks(boolean forceResizableTasks) {
- synchronized (mGlobalLock) {
- mForceResizableTasks = forceResizableTasks;
- }
- }
-
- public void setSupportsPictureInPicture(boolean supportsPictureInPicture) {
- synchronized (mGlobalLock) {
- mSupportsPictureInPicture = supportsPictureInPicture;
- }
- }
-
- public void setSupportsFreeformWindowManagement(boolean supportsFreeformWindowManagement) {
- synchronized (mGlobalLock) {
- mSupportsFreeformWindowManagement = supportsFreeformWindowManagement;
- }
- }
-
void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
synchronized (mGlobalLock) {
mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
}
}
- public void setIsPc(boolean isPc) {
+ @VisibleForTesting
+ void setIsPc(boolean isPc) {
synchronized (mGlobalLock) {
mIsPc = isPc;
}
@@ -6546,7 +6496,7 @@
"registerPinnedStackListener()")) {
return;
}
- if (!mSupportsPictureInPicture) {
+ if (!mAtmService.mSupportsPictureInPicture) {
return;
}
synchronized (mGlobalLock) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 466ca93..03f4755 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -165,6 +165,7 @@
outPointerIcon->bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
bitmapCopy->rowBytes(), 0, 0);
}
+ outSpriteIcon->style = outPointerIcon->style;
outSpriteIcon->hotSpotX = outPointerIcon->hotSpotX;
outSpriteIcon->hotSpotY = outPointerIcon->hotSpotY;
}
@@ -1252,7 +1253,8 @@
status_t status = android_view_PointerIcon_load(env, pointerIconObj.get(),
displayContext.get(), &pointerIcon);
if (!status && !pointerIcon.isNullIcon()) {
- *icon = SpriteIcon(pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
+ *icon = SpriteIcon(
+ pointerIcon.bitmap, pointerIcon.style, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
} else {
*icon = SpriteIcon();
}
@@ -1293,10 +1295,12 @@
milliseconds_to_nanoseconds(pointerIcon.durationPerFrame);
animationData.animationFrames.reserve(numFrames);
animationData.animationFrames.push_back(SpriteIcon(
- pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+ pointerIcon.bitmap, pointerIcon.style,
+ pointerIcon.hotSpotX, pointerIcon.hotSpotY));
for (size_t i = 0; i < numFrames - 1; ++i) {
animationData.animationFrames.push_back(SpriteIcon(
- pointerIcon.bitmapFrames[i], pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+ pointerIcon.bitmapFrames[i], pointerIcon.style,
+ pointerIcon.hotSpotX, pointerIcon.hotSpotY));
}
}
}
@@ -1711,6 +1715,7 @@
pointerIcon.bitmap.readPixels(spriteInfo, spriteIcon.bitmap.getPixels(),
spriteIcon.bitmap.rowBytes(), 0, 0);
}
+ spriteIcon.style = pointerIcon.style;
spriteIcon.hotSpotX = pointerIcon.hotSpotX;
spriteIcon.hotSpotY = pointerIcon.hotSpotY;
im->setCustomPointerIcon(spriteIcon);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ed900b1..67ae407 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -635,6 +635,13 @@
SystemServerInitThreadPool.get().submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
t.traceEnd();
+ // Platform compat service is used by ActivityManagerService, PackageManagerService, and
+ // possibly others in the future. b/135010838.
+ t.traceBegin("PlatformCompat");
+ ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE,
+ new PlatformCompat(mSystemContext));
+ t.traceEnd();
+
// Wait for installd to finish starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
// permissions. We need this to complete before we initialize other services.
@@ -1102,10 +1109,6 @@
SignedConfigService.registerUpdateReceiver(mSystemContext);
t.traceEnd();
- t.traceBegin("PlatformCompat");
- ServiceManager.addService("platform_compat", new PlatformCompat(context));
- t.traceEnd();
-
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index c1c0a30..57caa1d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2766,6 +2766,18 @@
}
@Test
+ public void testReadPolicyXml_readSnoozedNotificationsFromXml() throws Exception {
+ final String upgradeXml = "<notification-policy version=\"1\">"
+ + "<snoozed-notifications>></snoozed-notifications>"
+ + "</notification-policy>";
+ mService.readPolicyXml(
+ new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())),
+ false,
+ UserHandle.USER_ALL);
+ verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class));
+ }
+
+ @Test
public void testReadPolicyXml_readApprovedServicesFromSettings() throws Exception {
final String preupgradeXml = "<notification-policy version=\"1\">"
+ "<ranking></ranking>"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 2e7277f..36175a9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -18,6 +18,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -37,9 +38,11 @@
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.IntArray;
+import android.util.Xml;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
import org.junit.Before;
@@ -48,6 +51,15 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -69,6 +81,117 @@
}
@Test
+ public void testWriteXMLformattedCorrectly_testReadingCorrectTime()
+ throws XmlPullParserException, IOException {
+ final String max_time_str = Long.toString(Long.MAX_VALUE);
+ final String xml_string = "<snoozed-notifications>"
+ + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key\" time=\"" + max_time_str + "\"/>"
+ + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>"
+ + "</snoozed-notifications>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml_string.getBytes())), null);
+ mSnoozeHelper.readXml(parser);
+ assertTrue("Should read the notification time from xml and it should be more than zero",
+ 0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ 0, "pkg", "key").doubleValue());
+ }
+
+ @Test
+ public void testWriteXMLformattedCorrectly_testCorrectContextURI()
+ throws XmlPullParserException, IOException {
+ final String max_time_str = Long.toString(Long.MAX_VALUE);
+ final String xml_string = "<snoozed-notifications>"
+ + "<context version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key\" id=\"uri\"/>"
+ + "<context version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key2\" id=\"uri\"/>"
+ + "</snoozed-notifications>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml_string.getBytes())), null);
+ mSnoozeHelper.readXml(parser);
+ assertEquals("Should read the notification context from xml and it should be `uri",
+ "uri", mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+ 0, "pkg", "key"));
+ }
+
+ @Test
+ public void testReadValidSnoozedFromCorrectly_timeDeadline()
+ throws XmlPullParserException, IOException {
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 999999999);
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mSnoozeHelper.writeXml(serializer);
+ serializer.endDocument();
+ serializer.flush();
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+ mSnoozeHelper.readXml(parser);
+ assertTrue("Should read the notification time from xml and it should be more than zero",
+ 0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ 0, "pkg", r.getKey()).doubleValue());
+ }
+
+
+ @Test
+ public void testReadExpiredSnoozedNotification() throws
+ XmlPullParserException, IOException, InterruptedException {
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 0);
+ // Thread.sleep(100);
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mSnoozeHelper.writeXml(serializer);
+ serializer.endDocument();
+ serializer.flush();
+ Thread.sleep(10);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+ mSnoozeHelper.readXml(parser);
+ int systemUser = UserHandle.SYSTEM.getIdentifier();
+ assertTrue("Should see a past time returned",
+ System.currentTimeMillis() > mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ systemUser, "pkg", r.getKey()).longValue());
+ }
+
+ @Test
+ public void testCleanupContextShouldRemovePersistedRecord() {
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, "context");
+ mSnoozeHelper.cleanupPersistedContext(r.sbn.getKey());
+ assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+ r.getUser().getIdentifier(),
+ r.sbn.getPackageName(),
+ r.sbn.getKey()
+ ));
+ }
+
+ @Test
+ public void testReadNoneSnoozedNotification() throws XmlPullParserException,
+ IOException, InterruptedException {
+ NotificationRecord r = getNotificationRecord(
+ "pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 0);
+
+ assertEquals("should see a zero value for unsnoozed notification",
+ 0L,
+ mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ UserHandle.SYSTEM.getIdentifier(),
+ "not_my_package", r.getKey()).longValue());
+ }
+
+ @Test
public void testSnoozeForTime() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r, 1000);
@@ -84,7 +207,7 @@
@Test
public void testSnooze() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
- mSnoozeHelper.snooze(r);
+ mSnoozeHelper.snooze(r, (String) null);
verify(mAm, never()).setExactAndAllowWhileIdle(
anyInt(), anyLong(), any(PendingIntent.class));
assertTrue(mSnoozeHelper.isSnoozed(
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 63b9198..6c78f6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -28,6 +28,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
@@ -140,7 +141,8 @@
public void testNotResumeHomeStackOnRemovingDisplay() {
// Create a display which supports system decoration and allows reparenting stacks to
// another display when the display is removed.
- final ActivityDisplay display = spy(createNewActivityDisplay());
+ final ActivityDisplay display = createNewActivityDisplay();
+ spyOn(display);
doReturn(false).when(display).shouldDestroyContentOnRemove();
doReturn(true).when(display).supportsSystemDecorations();
mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP);
@@ -304,14 +306,18 @@
ACTIVITY_TYPE_STANDARD, ON_TOP);
final ActivityStack stack4 = display.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, ON_TOP);
- final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor).setStack(
- stack1).setTaskId(1).build();
- final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack(
- stack2).setTaskId(2).build();
- final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor).setStack(
- stack3).setTaskId(3).build();
- final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor).setStack(
- stack4).setTaskId(4).build();
+ final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(stack1)
+ .build();
+ final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(stack2)
+ .build();
+ final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(stack3)
+ .build();
+ final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(stack4)
+ .build();
// Reordering stacks while removing stacks.
doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 23bae88..977dd8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -61,13 +61,13 @@
private ActivityMetricsLaunchObserver mLaunchObserver;
private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
- private TestActivityStack mStack;
+ private ActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivityRecord;
private ActivityRecord mActivityRecordTrampoline;
@Before
- public void setUpAMLO() throws Exception {
+ public void setUpAMLO() {
mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
// ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger.
@@ -78,15 +78,19 @@
// Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
// This seems to be the easiest way to create an ActivityRecord.
- mStack = mRootActivityContainer.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
- mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
+ mStack = new StackBuilder(mRootActivityContainer)
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setOnTop(true)
+ .setCreateActivity(true)
+ .build();
+ mTask = mStack.topTask();
+ mActivityRecord = mTask.getTopActivity();
mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
}
@After
- public void tearDownAMLO() throws Exception {
+ public void tearDownAMLO() {
if (mLaunchObserverRegistry != null) { // Don't NPE if setUp failed.
mLaunchObserverRegistry.unregisterLaunchObserver(mLaunchObserver);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 7b252cb..0f04788 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,6 +16,12 @@
package com.android.server.wm;
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Process.NOBODY_UID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Surface.ROTATION_0;
@@ -29,6 +35,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
@@ -71,6 +78,7 @@
import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
import android.util.MutableBoolean;
+import android.view.DisplayInfo;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner.Stub;
import android.view.RemoteAnimationAdapter;
@@ -79,7 +87,6 @@
import androidx.test.filters.MediumTest;
import com.android.internal.R;
-import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Before;
import org.junit.Test;
@@ -96,13 +103,13 @@
@MediumTest
@Presubmit
public class ActivityRecordTests extends ActivityTestsBase {
- private TestActivityStack mStack;
+ private ActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
@Before
public void setUp() throws Exception {
- mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
+ mStack = new StackBuilder(mRootActivityContainer).build();
mTask = mStack.getChildAt(0);
mActivity = mTask.getTopActivity();
@@ -113,13 +120,13 @@
@Test
public void testStackCleanupOnClearingTask() {
mActivity.setTask(null);
- assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
+ verify(mStack, times(1)).onActivityRemovedFromStack(any());
}
@Test
public void testStackCleanupOnActivityRemoval() {
mTask.removeActivity(mActivity);
- assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
+ verify(mStack, times(1)).onActivityRemovedFromStack(any());
}
@Test
@@ -134,7 +141,7 @@
final TaskRecord newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack)
.build();
mActivity.reparent(newTask, 0, null /*reason*/);
- assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
+ verify(mStack, times(0)).onActivityRemovedFromStack(any());
}
@Test
@@ -181,7 +188,7 @@
assertTrue(mActivity.isState(STARTED));
mStack.mTranslucentActivityWaiting = null;
- topActivity.changeWindowTranslucency(false);
+ topActivity.setOccludesParent(false);
mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque");
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
assertTrue(mActivity.isState(STARTED));
@@ -261,8 +268,8 @@
public void testNewParentConfigurationIncrementsSeq() {
final Configuration newConfig = new Configuration(
mTask.getRequestedOverrideConfiguration());
- newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+ newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+ ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
mTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -277,7 +284,7 @@
.getRequestedOverrideConfiguration();
final Configuration newConfig = new Configuration();
- newConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
+ newConfig.orientation = ORIENTATION_PORTRAIT;
final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
mActivity.onRequestedOverrideConfigurationChanged(newConfig);
@@ -293,10 +300,11 @@
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
mActivity.getConfiguration()));
- mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+ mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(mTask.getConfiguration());
- newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+ newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+ ? ORIENTATION_LANDSCAPE
+ : ORIENTATION_PORTRAIT;
mTask.onRequestedOverrideConfigurationChanged(newConfig);
mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
@@ -315,13 +323,14 @@
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
mActivity.getConfiguration()));
- mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+ mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(mTask.getConfiguration());
- newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+ newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+ ? ORIENTATION_LANDSCAPE
+ : ORIENTATION_PORTRAIT;
mTask.onRequestedOverrideConfigurationChanged(newConfig);
- doReturn(true).when(mTask.getTask()).isDragResizing();
+ doReturn(true).when(mTask.mTask).isDragResizing();
mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
@@ -355,30 +364,39 @@
@Test
public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
+ mActivity = new ActivityBuilder(mService)
+ .setTask(mTask)
+ .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
+ .build();
mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
- mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
mActivity.getConfiguration()));
- mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(mActivity.getConfiguration());
- newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_LANDSCAPE
- : Configuration.ORIENTATION_PORTRAIT;
+ final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
+ final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
+ if (newConfig.orientation == ORIENTATION_PORTRAIT) {
+ newConfig.orientation = ORIENTATION_LANDSCAPE;
+ newConfig.screenWidthDp = longSide;
+ newConfig.screenHeightDp = shortSide;
+ } else {
+ newConfig.orientation = ORIENTATION_PORTRAIT;
+ newConfig.screenWidthDp = shortSide;
+ newConfig.screenHeightDp = longSide;
+ }
// Mimic the behavior that display doesn't handle app's requested orientation.
- doAnswer(invocation -> {
- mTask.onConfigurationChanged(newConfig);
- return null;
- }).when(mActivity.mAppWindowToken).setOrientation(anyInt(), any(), any());
+ final DisplayContent dc = mTask.mTask.getDisplayContent();
+ doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
+ doReturn(false).when(dc).handlesOrientationChangeFromDescendant();
final int requestedOrientation;
switch (newConfig.orientation) {
- case Configuration.ORIENTATION_LANDSCAPE:
- requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ case ORIENTATION_LANDSCAPE:
+ requestedOrientation = SCREEN_ORIENTATION_LANDSCAPE;
break;
- case Configuration.ORIENTATION_PORTRAIT:
+ case ORIENTATION_PORTRAIT:
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
default:
@@ -421,24 +439,33 @@
@Test
public void testPushConfigurationWhenLaunchTaskBehind() throws Exception {
+ mActivity = new ActivityBuilder(mService)
+ .setTask(mTask)
+ .setLaunchTaskBehind(true)
+ .setConfigChanges(CONFIG_ORIENTATION)
+ .build();
mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
- final TestActivityStack stack = (TestActivityStack) new StackBuilder(mRootActivityContainer)
- .build();
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
try {
- stack.setIsTranslucent(false);
+ doReturn(false).when(stack).isStackTranslucent(any());
assertFalse(mStack.shouldBeVisible(null /* starting */));
- mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
mActivity.getConfiguration()));
- mActivity.mLaunchTaskBehind = true;
- mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(mActivity.getConfiguration());
- newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_LANDSCAPE
- : Configuration.ORIENTATION_PORTRAIT;
+ final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
+ final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
+ if (newConfig.orientation == ORIENTATION_PORTRAIT) {
+ newConfig.orientation = ORIENTATION_LANDSCAPE;
+ newConfig.screenWidthDp = longSide;
+ newConfig.screenHeightDp = shortSide;
+ } else {
+ newConfig.orientation = ORIENTATION_PORTRAIT;
+ newConfig.screenWidthDp = shortSide;
+ newConfig.screenHeightDp = longSide;
+ }
mTask.onConfigurationChanged(newConfig);
@@ -457,7 +484,7 @@
@Test
public void testShouldPauseWhenMakeClientVisible() {
ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.changeWindowTranslucency(false);
+ topActivity.setOccludesParent(false);
mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
mActivity.makeClientVisible();
assertEquals(STARTED, mActivity.getState());
@@ -468,6 +495,7 @@
setupDisplayContentForCompatDisplayInsets();
final int decorHeight = 200; // e.g. The device has cutout.
final DisplayPolicy policy = setupDisplayAndParentSize(600, 800).getDisplayPolicy();
+ spyOn(policy);
doAnswer(invocationOnMock -> {
final int rotation = invocationOnMock.<Integer>getArgument(0);
final Rect insets = invocationOnMock.<Rect>getArgument(4);
@@ -482,7 +510,7 @@
doReturn(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
.when(mActivity.mAppWindowToken).getOrientationIgnoreVisibility();
- mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
ensureActivityConfiguration();
// The parent configuration doesn't change since the first resolved configuration, so the
@@ -506,19 +534,28 @@
@Test
public void testSizeCompatMode_FixedScreenConfigurationWhenMovingToDisplay() {
// Initialize different bounds on a new display.
- final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
- newDisplay.getWindowConfiguration().setAppBounds(new Rect(0, 0, 1000, 2000));
- newDisplay.getConfiguration().densityDpi = 300;
+ final Rect newDisplayBounds = new Rect(0, 0, 1000, 2000);
+ DisplayInfo info = new DisplayInfo();
+ mService.mContext.getDisplay().getDisplayInfo(info);
+ info.logicalWidth = newDisplayBounds.width();
+ info.logicalHeight = newDisplayBounds.height();
+ info.logicalDensityDpi = 300;
+
+ final ActivityDisplay newDisplay =
+ addNewActivityDisplayAt(info, ActivityDisplay.POSITION_TOP);
mTask.getConfiguration().densityDpi = 200;
- prepareFixedAspectRatioUnresizableActivity();
+ mActivity = new ActivityBuilder(mService)
+ .setTask(mTask)
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setMaxAspectRatio(1.5f)
+ .build();
final Rect originalBounds = new Rect(mActivity.getBounds());
final int originalDpi = mActivity.getConfiguration().densityDpi;
// Move the non-resizable activity to the new display.
mStack.reparent(newDisplay, true /* onTop */, false /* displayRemoved */);
- ensureActivityConfiguration();
assertEquals(originalBounds, mActivity.getBounds());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
@@ -531,9 +568,9 @@
when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
- mTask.getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+ mTask.getConfiguration().orientation = ORIENTATION_PORTRAIT;
mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
ensureActivityConfiguration();
final Rect originalBounds = new Rect(mActivity.getBounds());
@@ -579,7 +616,7 @@
mTask.getWindowConfiguration().setAppBounds(new Rect(0, 0, 600, 1200));
// Simulate the display changes orientation.
- doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION
+ doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
| ActivityInfo.CONFIG_WINDOW_CONFIGURATION)
.when(display).getLastOverrideConfigurationChanges();
mActivity.onConfigurationChanged(mTask.getConfiguration());
@@ -754,24 +791,17 @@
/** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
private void prepareFixedAspectRatioUnresizableActivity() {
setupDisplayContentForCompatDisplayInsets();
- when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
- ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.maxAspectRatio = 1.5f;
ensureActivityConfiguration();
}
private void setupDisplayContentForCompatDisplayInsets() {
final Rect displayBounds = mStack.getDisplay().getBounds();
- final DisplayContent displayContent = setupDisplayAndParentSize(
- displayBounds.width(), displayBounds.height());
- doReturn(mock(DisplayPolicy.class)).when(displayContent).getDisplayPolicy();
- doReturn(mock(WmDisplayCutout.class)).when(displayContent)
- .calculateDisplayCutoutForRotation(anyInt());
+ setupDisplayAndParentSize(displayBounds.width(), displayBounds.height());
}
private DisplayContent setupDisplayAndParentSize(int width, int height) {
- // The DisplayContent is already a mocked object.
final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
displayContent.mBaseDisplayWidth = width;
displayContent.mBaseDisplayHeight = height;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index e5278d8..ff7b1fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -29,7 +29,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
@@ -44,6 +44,7 @@
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
import static com.google.common.truth.Truth.assertThat;
@@ -83,8 +84,9 @@
@Before
public void setUp() throws Exception {
mDefaultDisplay = mRootActivityContainer.getDefaultDisplay();
- mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
- true /* onTop */));
+ mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ spyOn(mStack);
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
}
@@ -140,10 +142,8 @@
final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
-
- mTask.removeActivity(r);
- destTask.addActivityToTop(r);
+ mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_STACK_TO_FRONT, false, false,
+ "testResumedActivityFromActivityReparenting");
assertNull(mStack.getResumedActivity());
assertEquals(r, destStack.getResumedActivity());
@@ -313,45 +313,50 @@
@Test
public void testShouldBeVisible_Fullscreen() {
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+ final ActivityRecord pinnedActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setStack(pinnedStack)
+ .build();
assertTrue(homeStack.shouldBeVisible(null /* starting */));
assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
- final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
// should be visible since it is always on-top.
- fullscreenStack.setIsTranslucent(false);
+ doReturn(false).when(fullscreenStack).isStackTranslucent(any());
assertFalse(homeStack.shouldBeVisible(null /* starting */));
assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
// Home stack should be visible behind a translucent fullscreen stack.
- fullscreenStack.setIsTranslucent(true);
+ doReturn(true).when(fullscreenStack).isStackTranslucent(any());
assertTrue(homeStack.shouldBeVisible(null /* starting */));
assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
}
@Test
public void testShouldBeVisible_SplitScreen() {
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
// Home stack should always be fullscreen for this test.
- homeStack.setSupportsSplitScreen(false);
- final TestActivityStack splitScreenPrimary =
+ doReturn(false).when(homeStack).supportsSplitScreenWindowingMode();
+ final ActivityStack splitScreenPrimary =
createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final TestActivityStack splitScreenSecondary =
+ final ActivityStack splitScreenSecondary =
createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Home stack shouldn't be visible if both halves of split-screen are opaque.
- splitScreenPrimary.setIsTranslucent(false);
- splitScreenSecondary.setIsTranslucent(false);
+ doReturn(false).when(splitScreenPrimary).isStackTranslucent(any());
+ doReturn(false).when(splitScreenSecondary).isStackTranslucent(any());
assertFalse(homeStack.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -362,7 +367,7 @@
splitScreenSecondary.getVisibility(null /* starting */));
// Home stack should be visible if one of the halves of split-screen is translucent.
- splitScreenPrimary.setIsTranslucent(true);
+ doReturn(true).when(splitScreenPrimary).isStackTranslucent(any());
assertTrue(homeStack.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -373,12 +378,12 @@
assertEquals(STACK_VISIBILITY_VISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- final TestActivityStack splitScreenSecondary2 =
+ final ActivityStack splitScreenSecondary2 =
createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// First split-screen secondary shouldn't be visible behind another opaque split-split
// secondary.
- splitScreenSecondary2.setIsTranslucent(false);
+ doReturn(false).when(splitScreenSecondary2).isStackTranslucent(any());
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
assertEquals(STACK_VISIBILITY_INVISIBLE,
@@ -388,7 +393,7 @@
// First split-screen secondary should be visible behind another translucent split-screen
// secondary.
- splitScreenSecondary2.setIsTranslucent(true);
+ doReturn(true).when(splitScreenSecondary2).isStackTranslucent(any());
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -396,11 +401,11 @@
assertEquals(STACK_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
- final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
// Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
- assistantStack.setIsTranslucent(false);
+ doReturn(false).when(assistantStack).isStackTranslucent(any());
assertTrue(assistantStack.shouldBeVisible(null /* starting */));
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -415,7 +420,7 @@
splitScreenSecondary2.getVisibility(null /* starting */));
// Split-screen stacks should be visible behind a translucent fullscreen stack.
- assistantStack.setIsTranslucent(true);
+ doReturn(true).when(assistantStack).isStackTranslucent(any());
assertTrue(assistantStack.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -430,9 +435,9 @@
splitScreenSecondary2.getVisibility(null /* starting */));
// Assistant stack shouldn't be visible behind translucent split-screen stack
- assistantStack.setIsTranslucent(false);
- splitScreenPrimary.setIsTranslucent(true);
- splitScreenSecondary2.setIsTranslucent(true);
+ doReturn(false).when(assistantStack).isStackTranslucent(any());
+ doReturn(true).when(splitScreenPrimary).isStackTranslucent(any());
+ doReturn(true).when(splitScreenSecondary2).isStackTranslucent(any());
splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen");
splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen");
assertFalse(assistantStack.shouldBeVisible(null /* starting */));
@@ -450,10 +455,10 @@
@Test
public void testGetVisibility_FullscreenBehindTranslucent() {
- final TestActivityStack bottomStack =
+ final ActivityStack bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final TestActivityStack translucentStack =
+ final ActivityStack translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
@@ -465,13 +470,13 @@
@Test
public void testGetVisibility_FullscreenBehindTranslucentAndOpaque() {
- final TestActivityStack bottomStack =
+ final ActivityStack bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final TestActivityStack translucentStack =
+ final ActivityStack translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final TestActivityStack opaqueStack =
+ final ActivityStack opaqueStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
@@ -483,13 +488,13 @@
@Test
public void testGetVisibility_FullscreenBehindOpaqueAndTranslucent() {
- final TestActivityStack bottomStack =
+ final ActivityStack bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final TestActivityStack opaqueStack =
+ final ActivityStack opaqueStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final TestActivityStack translucentStack =
+ final ActivityStack translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
@@ -502,10 +507,10 @@
@Test
public void testGetVisibility_FullscreenTranslucentBehindTranslucent() {
- final TestActivityStack bottomTranslucentStack =
+ final ActivityStack bottomTranslucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final TestActivityStack translucentStack =
+ final ActivityStack translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
@@ -517,10 +522,10 @@
@Test
public void testGetVisibility_FullscreenTranslucentBehindOpaque() {
- final TestActivityStack bottomTranslucentStack =
+ final ActivityStack bottomTranslucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final TestActivityStack opaqueStack =
+ final ActivityStack opaqueStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
@@ -531,10 +536,10 @@
@Test
public void testGetVisibility_FullscreenBehindTranslucentAndPip() {
- final TestActivityStack bottomStack =
+ final ActivityStack bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final TestActivityStack translucentStack =
+ final ActivityStack translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
@@ -544,22 +549,34 @@
bottomStack.getVisibility(null /* starting */));
assertEquals(STACK_VISIBILITY_VISIBLE,
translucentStack.getVisibility(null /* starting */));
+ // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+ final ActivityRecord pinnedActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setStack(pinnedStack)
+ .build();
assertEquals(STACK_VISIBILITY_VISIBLE, pinnedStack.getVisibility(null /* starting */));
}
@Test
public void testShouldBeVisible_Finishing() {
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final TestActivityStack translucentStack = createStackForShouldBeVisibleTest(
+ ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+ if (topRunningHomeActivity == null) {
+ topRunningHomeActivity = new ActivityBuilder(mService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ }
+
+ final ActivityStack translucentStack = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- translucentStack.setIsTranslucent(true);
+ doReturn(true).when(translucentStack).isStackTranslucent(any());
assertTrue(homeStack.shouldBeVisible(null /* starting */));
assertTrue(translucentStack.shouldBeVisible(null /* starting */));
- final ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
topRunningHomeActivity.finishing = true;
final ActivityRecord topRunningTranslucentActivity =
translucentStack.topRunningActivityLocked();
@@ -577,13 +594,13 @@
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack.setIsTranslucent(false);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack).isStackTranslucent(any());
// Ensure that we don't move the home stack if it is already behind the top fullscreen stack
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -596,13 +613,13 @@
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack.setIsTranslucent(true);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(true).when(fullscreenStack).isStackTranslucent(any());
// Ensure that we don't move the home stack if it is already behind the top fullscreen stack
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -615,13 +632,13 @@
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack.setIsTranslucent(false);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack).isStackTranslucent(any());
// Ensure we don't move the home stack if it is already on top
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -634,20 +651,20 @@
public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack1.setIsTranslucent(false);
- fullscreenStack2.setIsTranslucent(false);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack2).isStackTranslucent(any());
// Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the
// pinned stack
@@ -661,18 +678,18 @@
testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack1.setIsTranslucent(false);
- fullscreenStack2.setIsTranslucent(true);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+ doReturn(true).when(fullscreenStack2).isStackTranslucent(any());
// Ensure that we move the home stack behind the bottom most non-translucent fullscreen
// stack
@@ -685,18 +702,18 @@
public void testMoveHomeStackBehindStack_BehindHomeStack() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack1.setIsTranslucent(false);
- fullscreenStack2.setIsTranslucent(false);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack2).isStackTranslucent(any());
// Ensure we don't move the home stack behind itself
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -708,19 +725,19 @@
public void testMoveHomeStackBehindStack() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack1);
@@ -735,13 +752,13 @@
@Test
public void testSetAlwaysOnTop() {
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(homeStack));
- final TestActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
+ final ActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
alwaysOnTopStack.setAlwaysOnTop(true);
@@ -749,13 +766,13 @@
// Ensure (non-pinned) always on top stack is put below pinned stack.
assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(alwaysOnTopStack));
- final TestActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
+ final ActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
// Ensure non always on top stack is put below always on top stacks.
assertEquals(alwaysOnTopStack, mDefaultDisplay.getStackAbove(nonAlwaysOnTopStack));
- final TestActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
+ final ActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
alwaysOnTopStack2.setAlwaysOnTop(true);
@@ -780,18 +797,18 @@
@Test
public void testSplitScreenMoveToFront() {
- final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
+ final ActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
+ final ActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
- splitScreenPrimary.setIsTranslucent(false);
- splitScreenSecondary.setIsTranslucent(false);
- assistantStack.setIsTranslucent(false);
+ doReturn(false).when(splitScreenPrimary).isStackTranslucent(any());
+ doReturn(false).when(splitScreenSecondary).isStackTranslucent(any());
+ doReturn(false).when(assistantStack).isStackTranslucent(any());
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -804,18 +821,18 @@
assertFalse(assistantStack.shouldBeVisible(null /* starting */));
}
- private TestActivityStack createStandardStackForVisibilityTest(int windowingMode,
+ private ActivityStack createStandardStackForVisibilityTest(int windowingMode,
boolean translucent) {
- final TestActivityStack stack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack stack = createStackForShouldBeVisibleTest(mDefaultDisplay,
windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- stack.setIsTranslucent(translucent);
+ doReturn(translucent).when(stack).isStackTranslucent(any());
return stack;
}
@SuppressWarnings("TypeParameterUnusedInFormals")
private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
- final T stack;
+ final ActivityStack stack;
if (activityType == ACTIVITY_TYPE_HOME) {
// Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
@@ -825,11 +842,15 @@
mDefaultDisplay.positionChildAtBottom(stack);
}
} else {
- stack = display.createStack(windowingMode, activityType, onTop);
- final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
- .setCreateTask(true).build();
+ stack = new StackBuilder(mRootActivityContainer)
+ .setDisplay(display)
+ .setWindowingMode(windowingMode)
+ .setActivityType(activityType)
+ .setOnTop(onTop)
+ .setCreateActivity(true)
+ .build();
}
- return stack;
+ return (T) stack;
}
@Test
@@ -961,11 +982,16 @@
@Test
public void testAdjustFocusedStackToHomeWhenNoActivity() {
+ final ActivityStack homeStask = mDefaultDisplay.getHomeStack();
+ TaskRecord homeTask = homeStask.topTask();
+ if (homeTask == null) {
+ // Create home task if there isn't one.
+ homeTask = new TaskBuilder(mSupervisor).setStack(homeStask).build();
+ }
+
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
mStack.moveToFront("testAdjustFocusedStack");
- final ActivityStack homeStask = mDefaultDisplay.getHomeStack();
- final TaskRecord homeTask = homeStask.topTask();
// Simulate that home activity has not been started or is force-stopped.
homeStask.removeTask(homeTask, "testAdjustFocusedStack", REMOVE_TASK_MODE_DESTROYING);
@@ -981,6 +1007,14 @@
final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ ActivityRecord activity = homeStack.topRunningActivityLocked();
+ if (activity == null) {
+ activity = new ActivityBuilder(mService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ }
+
// Home stack should not be destroyed immediately.
final ActivityRecord activity1 = finishCurrentActivity(homeStack);
assertEquals(FINISHING, activity1.getState());
@@ -1068,7 +1102,7 @@
public void testStackOrderChangedOnPositionStack() {
StackOrderChangedListener listener = new StackOrderChangedListener();
try {
- final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
mDefaultDisplay.registerStackOrderChangedListener(listener);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 3d94467..81fbfe4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -79,6 +79,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
+import com.android.server.wm.utils.MockTracker;
import org.junit.Before;
import org.junit.Test;
@@ -186,6 +187,19 @@
verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
}
+ private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
+ int expectedResult) {
+ // We track mocks created here because this is used in a single test
+ // (testStartActivityPreconditions) as a specific case, and mocks created inside it won't be
+ // used for other cases. To avoid extensive memory usage, we clean up all used mocks after
+ // each case. This is necessary because usually we only clean up mocks after a test
+ // finishes, but this test creates too many mocks that the intermediate memory usage can be
+ // ~0.8 GiB and thus very susceptible to OutOfMemoryException.
+ try (MockTracker tracker = new MockTracker()) {
+ verifyStartActivityPreconditionsUntracked(preconditions, launchFlags, expectedResult);
+ }
+ }
+
/**
* Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
* provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
@@ -197,7 +211,7 @@
* @param launchFlags The launch flags to be provided by the launch {@link Intent}.
* @param expectedResult The expected result from the launch.
*/
- private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
+ private void verifyStartActivityPreconditionsUntracked(int preconditions, int launchFlags,
int expectedResult) {
final ActivityTaskManagerService service = mService;
final IPackageManager packageManager = mock(IPackageManager.class);
@@ -329,9 +343,6 @@
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
anyBoolean(), anyBoolean(), any(), any(), any());
- // Use factory that only returns spy task.
- mockTaskRecordFactory();
-
if (mockGetLaunchStack) {
// Instrument the stack and task used.
final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
@@ -482,7 +493,7 @@
@Test
public void testTaskModeViolation() {
final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
- ((TestActivityDisplay) display).removeAllTasks();
+ display.removeAllTasks();
assertNoTasks(display);
final ActivityStarter starter = prepareStarter(0);
@@ -676,18 +687,27 @@
doReturn(isCallingUidDeviceOwner).when(mService).isDeviceOwner(callingUid);
final ActivityOptions options = spy(ActivityOptions.makeBasic());
+ ActivityRecord[] outActivity = new ActivityRecord[1];
ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
.setCallingPackage("com.whatever.dude")
.setCaller(caller)
.setCallingUid(callingUid)
.setRealCallingUid(realCallingUid)
- .setActivityOptions(new SafeActivityOptions(options));
+ .setActivityOptions(new SafeActivityOptions(options))
+ .setOutActivity(outActivity);
final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute();
assertEquals(ActivityStarter.getExternalResult(
shouldHaveAborted ? START_ABORTED : START_SUCCESS), result);
verify(options, times(shouldHaveAborted ? 1 : 0)).abort();
+
+ final ActivityRecord startedActivity = outActivity[0];
+ if (startedActivity != null && startedActivity.getTaskRecord() != null) {
+ // Remove the activity so it doesn't interfere with with subsequent activity launch
+ // tests from this method.
+ startedActivity.getTaskRecord().removeActivity(startedActivity);
+ }
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index d8a9bb0..297aa7e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -45,8 +45,7 @@
/** Verify that activity is finished correctly upon request. */
@Test
public void testActivityFinish() {
- final TestActivityStack stack =
- (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
final ActivityRecord activity = stack.getChildAt(0).getTopActivity();
assertTrue("Activity must be finished", mService.finishActivity(activity.appToken,
0 /* resultCode */, null /* resultData */,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index ab2da2b..a5dc241 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -16,96 +16,58 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
-import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
-import android.app.AppOpsManager;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerGlobal;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.Process;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.testing.DexmakerShareClassLoaderRule;
-import android.view.Display;
import android.view.DisplayInfo;
-import com.android.internal.app.IVoiceInteractor;
import com.android.server.AttributeCache;
-import com.android.server.ServiceThread;
-import com.android.server.am.ActivityManagerService;
-import com.android.server.am.PendingIntentController;
-import com.android.server.appop.AppOpsService;
-import com.android.server.firewall.IntentFirewall;
-import com.android.server.policy.PermissionPolicyInternal;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wm.TaskRecord.TaskRecordFactory;
-import com.android.server.wm.utils.MockTracker;
-import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.io.File;
-import java.util.List;
-import java.util.function.Consumer;
/**
* A base class to handle common operations in activity related unit tests.
*/
class ActivityTestsBase {
- private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
+
final Context mContext = getInstrumentation().getTargetContext();
- final TestInjector mTestInjector = new TestInjector(mContext);
ActivityTaskManagerService mService;
RootActivityContainer mRootActivityContainer;
ActivityStackSupervisor mSupervisor;
- private MockTracker mMockTracker;
-
// Default package name
static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
@@ -119,37 +81,18 @@
@Before
public void setUpBase() {
- mMockTracker = new MockTracker();
-
- mTestInjector.setUp();
-
- mService = new TestActivityTaskManagerService(mContext);
+ mService = mSystemServicesTestRule.getActivityTaskManagerService();
mSupervisor = mService.mStackSupervisor;
mRootActivityContainer = mService.mRootActivityContainer;
}
- @After
- public void tearDownBase() {
- mTestInjector.tearDown();
- if (mService != null) {
- mService.setWindowManager(null);
- mService = null;
- }
- if (sMockWindowManagerService != null) {
- reset(sMockWindowManagerService);
- }
-
- mMockTracker.close();
- mMockTracker = null;
- }
-
/** Creates a {@link TestActivityDisplay}. */
TestActivityDisplay createNewActivityDisplay() {
- return TestActivityDisplay.create(mSupervisor, sNextDisplayId++);
+ return TestActivityDisplay.create(mSupervisor);
}
TestActivityDisplay createNewActivityDisplay(DisplayInfo info) {
- return TestActivityDisplay.create(mSupervisor, sNextDisplayId++, info);
+ return TestActivityDisplay.create(mSupervisor, info);
}
/** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
@@ -166,25 +109,9 @@
return display;
}
- /**
- * Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy
- * when starting activity in unit tests.
- */
- void mockTaskRecordFactory(Consumer<TaskBuilder> taskBuilderSetup) {
- final TaskBuilder taskBuilder = new TaskBuilder(mSupervisor).setCreateStack(false);
- if (taskBuilderSetup != null) {
- taskBuilderSetup.accept(taskBuilder);
- }
- final TaskRecord task = taskBuilder.build();
- final TaskRecordFactory factory = mock(TaskRecordFactory.class);
- TaskRecord.setTaskRecordFactory(factory);
- doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */,
- any() /* info */, any() /* intent */, any() /* voiceSession */,
- any() /* voiceInteractor */);
- }
-
- void mockTaskRecordFactory() {
- mockTaskRecordFactory(null /* taskBuilderSetup */);
+ /** Sets the default minimum task size to 1 so that tests can use small task sizes */
+ public void removeGlobalMinSizeRestriction() {
+ mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
}
/**
@@ -204,6 +131,11 @@
private ActivityStack mStack;
private int mActivityFlags;
private int mLaunchMode;
+ private int mResizeMode = RESIZE_MODE_RESIZEABLE;
+ private float mMaxAspectRatio;
+ private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+ private boolean mLaunchTaskBehind;
+ private int mConfigChanges;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -254,6 +186,31 @@
return this;
}
+ ActivityBuilder setResizeMode(int resizeMode) {
+ mResizeMode = resizeMode;
+ return this;
+ }
+
+ ActivityBuilder setMaxAspectRatio(float maxAspectRatio) {
+ mMaxAspectRatio = maxAspectRatio;
+ return this;
+ }
+
+ ActivityBuilder setScreenOrientation(int screenOrientation) {
+ mScreenOrientation = screenOrientation;
+ return this;
+ }
+
+ ActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) {
+ mLaunchTaskBehind = launchTaskBehind;
+ return this;
+ }
+
+ ActivityBuilder setConfigChanges(int configChanges) {
+ mConfigChanges = configChanges;
+ return this;
+ }
+
ActivityRecord build() {
if (mComponent == null) {
final int id = sCurrentActivityId++;
@@ -279,24 +236,31 @@
}
aInfo.flags |= mActivityFlags;
aInfo.launchMode = mLaunchMode;
+ aInfo.resizeMode = mResizeMode;
+ aInfo.maxAspectRatio = mMaxAspectRatio;
+ aInfo.screenOrientation = mScreenOrientation;
+ aInfo.configChanges |= mConfigChanges;
+
+ ActivityOptions options = null;
+ if (mLaunchTaskBehind) {
+ options = ActivityOptions.makeTaskLaunchBehind();
+ }
final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
0 /* launchedFromPid */, 0, null, intent, null,
aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
- mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
+ mService.mStackSupervisor, options, null /* sourceRecord */);
spyOn(activity);
- activity.mAppWindowToken = mock(AppWindowToken.class);
- doCallRealMethod().when(activity.mAppWindowToken).getOrientationIgnoreVisibility();
- doCallRealMethod().when(activity.mAppWindowToken)
- .setOrientation(anyInt(), any(), any());
- doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt());
- doNothing().when(activity).removeWindowContainer();
- doReturn(mock(Configuration.class)).when(activity.mAppWindowToken)
- .getRequestedOverrideConfiguration();
-
if (mTaskRecord != null) {
- mTaskRecord.addActivityToTop(activity);
+ // fullscreen value is normally read from resources in ctor, so for testing we need
+ // to set it somewhere else since we can't mock resources.
+ activity.fullscreen = true;
+ activity.setTask(mTaskRecord);
+ activity.createAppWindowToken();
+ spyOn(activity.mAppWindowToken);
+ // Make visible by default...
+ activity.mAppWindowToken.setHidden(false);
}
final WindowProcessController wpc = new WindowProcessController(mService,
@@ -305,6 +269,9 @@
mock(WindowProcessListener.class));
wpc.setThread(mock(IApplicationThread.class));
activity.setProcess(wpc);
+
+ // Resume top activities to make sure all other signals in the system are connected.
+ mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
return activity;
}
}
@@ -313,16 +280,13 @@
* Builder for creating new tasks.
*/
protected static class TaskBuilder {
- // Default package name
- static final String DEFAULT_PACKAGE = "com.bar";
-
private final ActivityStackSupervisor mSupervisor;
private ComponentName mComponent;
private String mPackage;
private int mFlags = 0;
// Task id 0 is reserved in ARC for the home app.
- private int mTaskId = 1;
+ private int mTaskId = SystemServicesTestRule.sNextTaskId++;
private int mUserId = 0;
private IVoiceInteractionSession mVoiceSession;
private boolean mCreateStack = true;
@@ -381,6 +345,7 @@
if (mStack == null && mCreateStack) {
mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ spyOn(mStack);
}
final ActivityInfo aInfo = new ActivityInfo();
@@ -396,450 +361,22 @@
intent.setComponent(mComponent);
intent.setFlags(mFlags);
- final TestTaskRecord task = new TestTaskRecord(mSupervisor.mService, mTaskId, aInfo,
+ final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
+ spyOn(task);
task.userId = mUserId;
if (mStack != null) {
mStack.moveToFront("test");
mStack.addTask(task, true, "creating test task");
- task.setStack(mStack);
- task.setTask();
- mStack.getTaskStack().addChild(task.mTask, 0);
+ task.createTask(true, true);
+ spyOn(task.mTask);
}
task.touchActiveTime();
return task;
}
-
- private static class TestTaskRecord extends TaskRecord {
- TestTaskRecord(ActivityTaskManagerService service, int taskId, ActivityInfo info,
- Intent intent, IVoiceInteractionSession voiceSession,
- IVoiceInteractor voiceInteractor) {
- super(service, taskId, info, intent, voiceSession, voiceInteractor);
- }
-
- @Override
- void createTask(boolean onTop, boolean showForAllUsers) {
- setTask();
- }
-
- void setTask() {
- Task mockTask = mock(Task.class);
- mockTask.mTaskRecord = this;
- doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any());
- setTask(mock(Task.class));
- }
- }
- }
-
- protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
- private PackageManagerInternal mPmInternal;
- private PermissionPolicyInternal mPermissionPolicyInternal;
-
- // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
- // We keep the reference in order to prevent creating it twice.
- ActivityStackSupervisor mTestStackSupervisor;
-
- ActivityDisplay mDefaultDisplay;
- AppOpsService mAppOpsService;
-
- TestActivityTaskManagerService(Context context) {
- super(context);
- spyOn(this);
-
- mUgmInternal = mock(UriGrantsManagerInternal.class);
- mAppOpsService = mock(AppOpsService.class);
-
- // Make sure permission checks aren't overridden.
- doReturn(AppOpsManager.MODE_DEFAULT)
- .when(mAppOpsService).noteOperation(anyInt(), anyInt(), anyString());
-
- mSupportsMultiWindow = true;
- mSupportsMultiDisplay = true;
- mSupportsSplitScreenMultiWindow = true;
- mSupportsFreeformWindowManagement = true;
- mSupportsPictureInPicture = true;
-
- final TestActivityManagerService am =
- new TestActivityManagerService(mTestInjector, this);
-
- spyOn(getLifecycleManager());
- spyOn(getLockTaskController());
- spyOn(getTaskChangeNotificationController());
- doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
- // allow background activity starts by default
- doReturn(true).when(this).isBackgroundActivityStartsEnabled();
- doNothing().when(this).updateCpuStats();
- }
-
- void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
- ActivityManagerInternal amInternal, WindowManagerService wm, Looper looper) {
- mAmInternal = amInternal;
- initialize(intentFirewall, intentController, looper);
- initRootActivityContainerMocks(wm);
- setWindowManager(wm);
- createDefaultDisplay();
- }
-
- void initRootActivityContainerMocks(WindowManagerService wm) {
- spyOn(mRootActivityContainer);
- mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class));
- mRootActivityContainer.mWindowManager = wm;
- mRootActivityContainer.mDisplayManager =
- (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
- doNothing().when(mRootActivityContainer).setWindowManager(any());
- // Invoked during {@link ActivityStack} creation.
- doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
- // Always keep things awake.
- doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
- // Called when moving activity to pinned stack.
- doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
- anyBoolean());
- }
-
- void createDefaultDisplay() {
- // Create a default display and put a home stack on it so that we'll always have
- // something focusable.
- mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY);
- spyOn(mDefaultDisplay);
- mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP);
- mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final TaskRecord task = new TaskBuilder(mStackSupervisor)
- .setStack(mDefaultDisplay.getHomeStack()).build();
- new ActivityBuilder(this).setTask(task).build();
-
- doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay();
- }
-
- @Override
- int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
- return userId;
- }
-
- @Override
- AppOpsService getAppOpsService() {
- return mAppOpsService;
- }
-
- @Override
- void updateCpuStats() {
- }
-
- @Override
- void updateBatteryStats(ActivityRecord component, boolean resumed) {
- }
-
- @Override
- void updateActivityUsageStats(ActivityRecord activity, int event) {
- }
-
- @Override
- protected ActivityStackSupervisor createStackSupervisor() {
- if (mTestStackSupervisor == null) {
- mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
- }
- return mTestStackSupervisor;
- }
-
- @Override
- PackageManagerInternal getPackageManagerInternalLocked() {
- if (mPmInternal == null) {
- mPmInternal = mock(PackageManagerInternal.class);
- doReturn(false)
- .when(mPmInternal)
- .isPermissionsReviewRequired(anyString(), anyInt());
- }
- return mPmInternal;
- }
-
- @Override
- PermissionPolicyInternal getPermissionPolicyInternal() {
- if (mPermissionPolicyInternal == null) {
- mPermissionPolicyInternal = mock(PermissionPolicyInternal.class);
- doReturn(true).when(mPermissionPolicyInternal).checkStartActivity(any(), anyInt(),
- any());
- }
- return mPermissionPolicyInternal;
- }
- }
-
- private static class TestInjector extends ActivityManagerService.Injector {
- private ServiceThread mHandlerThread;
-
- TestInjector(Context context) {
- super(context);
- }
-
- @Override
- public AppOpsService getAppOpsService(File file, Handler handler) {
- return null;
- }
-
- @Override
- public Handler getUiHandler(ActivityManagerService service) {
- return mHandlerThread.getThreadHandler();
- }
-
- @Override
- public boolean isNetworkRestrictedForUid(int uid) {
- return false;
- }
-
- void setUp() {
- mHandlerThread = new ServiceThread("ActivityTestsThread",
- Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
- mHandlerThread.start();
- }
-
- void tearDown() {
- // Make sure there are no running messages and then quit the thread so the next test
- // won't be affected.
- mHandlerThread.getThreadHandler().runWithScissors(mHandlerThread::quit,
- 0 /* timeout */);
- }
- }
-
- // TODO: Replace this with a mock object since we are no longer in AMS package.
- /**
- * An {@link ActivityManagerService} subclass which provides a test
- * {@link ActivityStackSupervisor}.
- */
- class TestActivityManagerService extends ActivityManagerService {
-
- TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) {
- super(testInjector, testInjector.mHandlerThread);
- spyOn(this);
-
- mWindowManager = prepareMockWindowManager();
- mUgmInternal = mock(UriGrantsManagerInternal.class);
-
- atm.setup(mIntentFirewall, mPendingIntentController, new LocalService(), mWindowManager,
- testInjector.mHandlerThread.getLooper());
-
- mActivityTaskManager = atm;
- mAtmInternal = atm.mInternal;
-
- doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
- PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
- doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked();
- doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
- doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
- }
- }
-
- /**
- * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
- * setup not available in the test environment. Also specifies an injector for
- */
- protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
- private KeyguardController mKeyguardController;
-
- TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
- super(service, looper);
- spyOn(this);
- mWindowManager = prepareMockWindowManager();
- mKeyguardController = mock(KeyguardController.class);
-
- // Do not schedule idle that may touch methods outside the scope of the test.
- doNothing().when(this).scheduleIdleLocked();
- doNothing().when(this).scheduleIdleTimeoutLocked(any());
- // unit test version does not handle launch wake lock
- doNothing().when(this).acquireLaunchWakelock();
- doReturn(mKeyguardController).when(this).getKeyguardController();
-
- mLaunchingActivityWakeLock = mock(PowerManager.WakeLock.class);
-
- initialize();
- }
-
- @Override
- public KeyguardController getKeyguardController() {
- return mKeyguardController;
- }
-
- @Override
- void setWindowManager(WindowManagerService wm) {
- mWindowManager = wm;
- }
- }
-
- protected static class TestActivityDisplay extends ActivityDisplay {
- private final ActivityStackSupervisor mSupervisor;
-
- static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
- return create(supervisor, displayId, new DisplayInfo());
- }
-
- static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId,
- DisplayInfo info) {
- if (displayId == DEFAULT_DISPLAY) {
- return new TestActivityDisplay(supervisor,
- supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
- }
- final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
- info, DEFAULT_DISPLAY_ADJUSTMENTS);
- return new TestActivityDisplay(supervisor, display);
- }
-
- TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
- super(supervisor.mService.mRootActivityContainer, display);
- // Normally this comes from display-properties as exposed by WM. Without that, just
- // hard-code to FULLSCREEN for tests.
- setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- mSupervisor = supervisor;
- }
-
- @SuppressWarnings("TypeParameterUnusedInFormals")
- @Override
- ActivityStack createStackUnchecked(int windowingMode, int activityType,
- int stackId, boolean onTop) {
- return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this)
- .setWindowingMode(windowingMode).setActivityType(activityType)
- .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
- }
-
- @Override
- protected DisplayContent createDisplayContent() {
- final DisplayContent displayContent = mock(DisplayContent.class);
- DockedStackDividerController divider = mock(DockedStackDividerController.class);
- doReturn(divider).when(displayContent).getDockedDividerController();
- return displayContent;
- }
-
- void removeAllTasks() {
- for (int i = 0; i < getChildCount(); i++) {
- final ActivityStack stack = getChildAt(i);
- for (TaskRecord task : (List<TaskRecord>) stack.getAllTasks()) {
- stack.removeTask(task, "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
- }
- }
- }
- }
-
- private static WindowManagerService sMockWindowManagerService;
-
- private static WindowManagerService prepareMockWindowManager() {
- if (sMockWindowManagerService == null) {
- sMockWindowManagerService = mock(WindowManagerService.class);
- }
-
- sMockWindowManagerService.mRoot = mock(RootWindowContainer.class);
-
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
- if (runnable != null) {
- runnable.run();
- }
- return null;
- }).when(sMockWindowManagerService).inSurfaceTransaction(any());
-
- return sMockWindowManagerService;
- }
-
- /**
- * Overridden {@link ActivityStack} that tracks test metrics, such as the number of times a
- * method is called. Note that its functionality depends on the implementations of the
- * construction arguments.
- */
- protected static class TestActivityStack
- extends ActivityStack {
- private int mOnActivityRemovedFromStackCount = 0;
-
- static final int IS_TRANSLUCENT_UNSET = 0;
- static final int IS_TRANSLUCENT_FALSE = 1;
- static final int IS_TRANSLUCENT_TRUE = 2;
- private int mIsTranslucent = IS_TRANSLUCENT_UNSET;
-
- static final int SUPPORTS_SPLIT_SCREEN_UNSET = 0;
- static final int SUPPORTS_SPLIT_SCREEN_FALSE = 1;
- static final int SUPPORTS_SPLIT_SCREEN_TRUE = 2;
- private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET;
-
- TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
- int windowingMode, int activityType, boolean onTop, boolean createActivity) {
- super(display, stackId, supervisor, windowingMode, activityType, onTop);
- if (createActivity) {
- new ActivityBuilder(mService).setCreateTask(true).setStack(this).build();
- if (onTop) {
- // We move the task to front again in order to regain focus after activity
- // added to the stack. Or {@link ActivityDisplay#mPreferredTopFocusableStack}
- // could be other stacks (e.g. home stack).
- moveToFront("createActivityStack");
- } else {
- moveToBack("createActivityStack", null);
- }
- }
- }
-
- @Override
- void onActivityRemovedFromStack(ActivityRecord r) {
- mOnActivityRemovedFromStackCount++;
- super.onActivityRemovedFromStack(r);
- }
-
- // Returns the number of times {@link #onActivityRemovedFromStack} has been called
- int onActivityRemovedFromStackInvocationCount() {
- return mOnActivityRemovedFromStackCount;
- }
-
- @Override
- protected void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
- mTaskStack = mock(TaskStack.class);
-
- // Primary pinned stacks require a non-empty out bounds to be set or else all tasks
- // will be moved to the full screen stack.
- if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- outBounds.set(0, 0, 100, 100);
- }
- }
-
- @Override
- TaskStack getTaskStack() {
- return mTaskStack;
- }
-
- void setIsTranslucent(boolean isTranslucent) {
- mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE;
- }
-
- @Override
- boolean isStackTranslucent(ActivityRecord starting) {
- switch (mIsTranslucent) {
- case IS_TRANSLUCENT_TRUE:
- return true;
- case IS_TRANSLUCENT_FALSE:
- return false;
- case IS_TRANSLUCENT_UNSET:
- default:
- return super.isStackTranslucent(starting);
- }
- }
-
- void setSupportsSplitScreen(boolean supportsSplitScreen) {
- mSupportsSplitScreen = supportsSplitScreen
- ? SUPPORTS_SPLIT_SCREEN_TRUE : SUPPORTS_SPLIT_SCREEN_FALSE;
- }
-
- @Override
- public boolean supportsSplitScreenWindowingMode() {
- switch (mSupportsSplitScreen) {
- case SUPPORTS_SPLIT_SCREEN_TRUE:
- return true;
- case SUPPORTS_SPLIT_SCREEN_FALSE:
- return false;
- case SUPPORTS_SPLIT_SCREEN_UNSET:
- default:
- return super.supportsSplitScreenWindowingMode();
- }
- }
-
- @Override
- void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
- boolean newTask, boolean keepCurTransition,
- ActivityOptions options) {
- }
}
static class StackBuilder {
@@ -886,27 +423,45 @@
return this;
}
- @SuppressWarnings("TypeParameterUnusedInFormals")
ActivityStack build() {
final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
+ final ActivityStack stack;
+ final ActivityStackSupervisor supervisor = mRootActivityContainer.mStackSupervisor;
if (mWindowingMode == WINDOWING_MODE_PINNED) {
- return new ActivityStack(mDisplay, stackId, mRootActivityContainer.mStackSupervisor,
+ stack = new ActivityStack(mDisplay, stackId, supervisor,
mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) {
@Override
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
return new Rect(50, 50, 100, 100);
}
-
- @Override
- void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
- mTaskStack = mock(TaskStack.class);
- }
};
} else {
- return new TestActivityStack(mDisplay, stackId,
- mRootActivityContainer.mStackSupervisor, mWindowingMode,
- mActivityType, mOnTop, mCreateActivity);
+ stack = new ActivityStack(mDisplay, stackId, supervisor,
+ mWindowingMode, mActivityType, mOnTop);
+
+ if (mCreateActivity) {
+ new ActivityBuilder(supervisor.mService)
+ .setCreateTask(true)
+ .setStack(stack)
+ .build();
+ if (mOnTop) {
+ // We move the task to front again in order to regain focus after activity
+ // added to the stack.
+ // Or {@link ActivityDisplay#mPreferredTopFocusableStack} could be other
+ // stacks (e.g. home stack).
+ stack.moveToFront("createActivityStack");
+ } else {
+ stack.moveToBack("createActivityStack", null);
+ }
+ }
}
+
+ spyOn(stack);
+ spyOn(stack.mTaskStack);
+ doNothing().when(stack).startActivityLocked(
+ any(), any(), anyBoolean(), anyBoolean(), any());
+
+ return stack;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 20379a2..e71c8f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -60,7 +60,7 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- translucentOpening.setFillsParent(false);
+ translucentOpening.setOccludesParent(false);
translucentOpening.setHidden(true);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
@@ -78,7 +78,7 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- translucentClosing.setFillsParent(false);
+ translucentClosing.setOccludesParent(false);
mDisplayContent.mClosingApps.add(translucentClosing);
assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
@@ -94,7 +94,7 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- translucentOpening.setFillsParent(false);
+ translucentOpening.setOccludesParent(false);
translucentOpening.setHidden(true);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
@@ -110,10 +110,10 @@
synchronized (mWm.mGlobalLock) {
final AppWindowToken opening = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
- opening.setFillsParent(false);
+ opening.setOccludesParent(false);
final AppWindowToken closing = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
- closing.setFillsParent(false);
+ closing.setOccludesParent(false);
Task task = opening.getTask();
mDisplayContent.mOpeningApps.add(opening);
mDisplayContent.mClosingApps.add(closing);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index d1dc382..c162b6a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -30,12 +30,14 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import android.graphics.Rect;
import android.os.IBinder;
@@ -195,6 +197,7 @@
@Test
public void testCancelRemoteAnimationWhenFreeze() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+ doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
dc, "exiting app");
final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index d9566a3..e387e18 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -36,7 +36,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
@@ -152,10 +152,6 @@
@Test
@FlakyTest(bugId = 131005232)
public void testLandscapeSeascapeRotationByApp() {
- // Some plumbing to get the service ready for rotation updates.
- mWm.mDisplayReady = true;
- mWm.mDisplayEnabled = true;
-
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
@@ -185,25 +181,21 @@
@Test
public void testLandscapeSeascapeRotationByPolicy() {
- // Some plumbing to get the service ready for rotation updates.
- mWm.mDisplayReady = true;
- mWm.mDisplayEnabled = true;
-
- final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
- mDisplayContent.setDisplayRotation(spiedRotation);
+ final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+ spyOn(displayRotation);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
- attrs.setTitle("AppWindow");
+ attrs.setTitle("RotationByPolicy");
final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
mToken.addWindow(appWindow);
// Set initial orientation and update.
- performRotation(spiedRotation, Surface.ROTATION_90);
+ performRotation(displayRotation, Surface.ROTATION_90);
appWindow.mResizeReported = false;
// Update the rotation to perform 180 degree rotation and check that resize was reported.
- performRotation(spiedRotation, Surface.ROTATION_270);
+ performRotation(displayRotation, Surface.ROTATION_270);
assertTrue(appWindow.mResizeReported);
appWindow.removeImmediately();
@@ -211,14 +203,7 @@
private void performRotation(DisplayRotation spiedRotation, int rotationToReport) {
doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt());
- int oldRotation = mDisplayContent.getRotation();
mWm.updateRotation(false, false);
- // Must manually apply here since ATM doesn't know about the display during this test
- // (meaning it can't perform the normal sendNewConfiguration flow).
- mDisplayContent.applyRotationLocked(oldRotation, mDisplayContent.getRotation());
- // Prevent the next rotation from being deferred by animation.
- mWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
- mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
}
@Test
@@ -266,14 +251,14 @@
public void testGetOrientation() {
mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- mToken.setFillsParent(false);
- // Can specify orientation if app doesn't fill parent.
+ mToken.setOccludesParent(false);
+ // Can specify orientation if app doesn't occludes parent.
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
- mToken.setFillsParent(true);
+ mToken.setOccludesParent(true);
mToken.setHidden(true);
mToken.sendingToBottom = true;
- // Can not specify orientation if app isn't visible even though it fills parent.
+ // Can not specify orientation if app isn't visible even though it occludes parent.
assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
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 388658d..6289768 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -83,7 +83,6 @@
import androidx.test.filters.SmallTest;
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -653,25 +652,27 @@
@Test
public void testOnDescendantOrientationRequestChanged() {
final DisplayContent dc = createNewDisplay();
- mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+ dc.getDisplayRotation().setFixedToUserRotation(
+ DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED);
final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
? SCREEN_ORIENTATION_PORTRAIT
: SCREEN_ORIENTATION_LANDSCAPE;
- final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
- window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
- window.mAppToken.setOrientation(newOrientation);
+ final ActivityStack stack =
+ new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
+ .setDisplay(dc.mAcitvityDisplay).build();
+ final ActivityRecord activity = stack.topTask().getTopActivity();
- ActivityRecord activityRecord = mock(ActivityRecord.class);
-
- assertTrue("Display should rotate to handle orientation request by default.",
- dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+ activity.setRequestedOrientation(newOrientation);
final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
verify(dc.mAcitvityDisplay).updateDisplayOverrideConfigurationLocked(captor.capture(),
- same(activityRecord), anyBoolean(), same(null));
+ same(activity), anyBoolean(), same(null));
final Configuration newDisplayConfig = captor.getValue();
- assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
+ final int expectedOrientation = newOrientation == SCREEN_ORIENTATION_PORTRAIT
+ ? Configuration.ORIENTATION_PORTRAIT
+ : Configuration.ORIENTATION_LANDSCAPE;
+ assertEquals(expectedOrientation, newDisplayConfig.orientation);
}
@Test
@@ -679,22 +680,20 @@
final DisplayContent dc = createNewDisplay();
dc.getDisplayRotation().setFixedToUserRotation(
DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
- mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
? SCREEN_ORIENTATION_PORTRAIT
: SCREEN_ORIENTATION_LANDSCAPE;
- final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
- window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
- window.mAppToken.setOrientation(newOrientation);
+ final ActivityStack stack =
+ new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
+ .setDisplay(dc.mAcitvityDisplay).build();
+ final ActivityRecord activity = stack.topTask().getTopActivity();
- ActivityRecord activityRecord = mock(ActivityRecord.class);
+ activity.setRequestedOrientation(newOrientation);
- assertFalse("Display shouldn't rotate to handle orientation request if fixed to"
- + " user rotation.",
- dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
verify(dc.mAcitvityDisplay, never()).updateDisplayOverrideConfigurationLocked(any(),
- eq(activityRecord), anyBoolean(), same(null));
+ eq(activity), anyBoolean(), same(null));
+ assertEquals(dc.getDisplayRotation().getUserRotation(), dc.getRotation());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index bfede51..f6f8811 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -95,7 +95,7 @@
public void setUp() throws Exception {
deleteRecursively(TEST_FOLDER);
- mWm.setSupportsFreeformWindowManagement(false);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = false;
mWm.setIsPc(false);
mWm.setForceDesktopModeOnExternalDisplays(false);
@@ -134,7 +134,7 @@
@Test
public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_NoDesktopMode() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -144,7 +144,7 @@
@Test
public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_HasDesktopMode() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.setForceDesktopModeOnExternalDisplays(true);
mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -155,7 +155,7 @@
@Test
public void testPrimaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.setIsPc(true);
mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -168,7 +168,7 @@
public void testPrimaryDisplayUpdateToFreeform_HasFreeformSupport_IsPc() {
mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.setIsPc(true);
mTarget.updateSettingsForDisplay(mPrimaryDisplay);
@@ -187,7 +187,7 @@
@Test
public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_NoDesktopMode() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
@@ -197,7 +197,7 @@
@Test
public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_HasDesktopMode() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.setForceDesktopModeOnExternalDisplays(true);
mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
@@ -208,7 +208,7 @@
@Test
public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.setIsPc(true);
mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index b28ae40..be2ee29 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -331,7 +331,9 @@
mController.layoutTask(task, null /* windowLayout */);
- assertEquals(expected, task.getBounds());
+ // TaskRecord will make adjustments to requested bounds. We only need to guarantee that the
+ // reuqested bounds are expected.
+ assertEquals(expected, task.getRequestedOverrideBounds());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index e4d3770..49d778f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -104,6 +104,7 @@
mDisplayUniqueId = "test:" + Integer.toString(sNextUniqueId++);
final DisplayInfo info = new DisplayInfo();
+ mService.mContext.getDisplay().getDisplayInfo(info);
info.uniqueId = mDisplayUniqueId;
mTestDisplay = createNewActivityDisplay(info);
mRootActivityContainer.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index 63d9fb9..efd468f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -61,7 +61,7 @@
@Test
public void setShelfHeight_shelfVisibilityChangedTriggered() throws RemoteException {
- mWm.mSupportsPictureInPicture = true;
+ mWm.mAtmService.mSupportsPictureInPicture = true;
mWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener);
verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index b7a85d7..fb4e330 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -32,7 +32,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -53,14 +53,12 @@
import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
import android.content.ComponentName;
-import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -93,11 +91,8 @@
private static final int TEST_QUIET_USER_ID = 20;
private static final UserInfo DEFAULT_USER_INFO = new UserInfo();
private static final UserInfo QUIET_USER_INFO = new UserInfo();
- private static int sLastTaskId = 1;
- private static int sLastStackId = 1;
private static final int INVALID_STACK_ID = 999;
- private TestActivityTaskManagerService mTestService;
private ActivityDisplay mDisplay;
private ActivityDisplay mOtherDisplay;
private ActivityDisplay mSingleTaskDisplay;
@@ -115,13 +110,29 @@
@Before
public void setUp() throws Exception {
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
- mTestService = new MyTestActivityTaskManagerService(mContext);
- mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
+
+ // Set testing displays
+ mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
+ mOtherDisplay = createNewActivityDisplay();
+ mSingleTaskDisplay = createNewActivityDisplay();
+ mSingleTaskDisplay.setDisplayToSingleTaskInstance();
+ mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
+ mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+ mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
+
+ // Set the recent tasks we should use for testing in this class.
+ mRecentTasks = new TestRecentTasks(mService, mTaskPersister);
+ spyOn(mRecentTasks);
+ mService.setRecentTasks(mRecentTasks);
mRecentTasks.loadParametersFromResources(mContext.getResources());
- mRunningTasks = (TestRunningTasks) mTestService.mStackSupervisor.mRunningTasks;
- mHomeStack = mTestService.mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
+
+ // Set the running tasks we should use for testing in this class.
+ mRunningTasks = new TestRunningTasks();
+ mService.mStackSupervisor.setRunningTasks(mRunningTasks);
+
+ mHomeStack = mDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- mStack = mTestService.mRootActivityContainer.getDefaultDisplay().createStack(
+ mStack = mDisplay.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mCallbacksRecorder = new CallbacksRecorder();
mRecentTasks.registerCallback(mCallbacksRecorder);
@@ -723,7 +734,7 @@
ActivityStack stack = mTasks.get(2).getStack();
stack.moveToFront("", mTasks.get(2));
- doReturn(stack).when(mTestService.mRootActivityContainer).getTopDisplayFocusedStack();
+ doReturn(stack).when(mService.mRootActivityContainer).getTopDisplayFocusedStack();
// Simulate the reset from the timeout
mRecentTasks.resetFreezeTaskListReorderingOnTimeout();
@@ -742,10 +753,9 @@
public void testBackStackTasks_expectNoTrim() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
- final MyTestActivityStackSupervisor supervisor =
- (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
final ActivityStack homeStack = mDisplay.getHomeStack();
- final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack aboveHomeStack = mDisplay.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
// the tasks belong in stacks above the home stack
@@ -761,11 +771,11 @@
public void testBehindHomeStackTasks_expectTaskTrimmed() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
- final MyTestActivityStackSupervisor supervisor =
- (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
- final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack behindHomeStack = mDisplay.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityStack homeStack = mDisplay.getHomeStack();
- final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack aboveHomeStack = mDisplay.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
// the home stack is trimmed once a new task is added
@@ -783,10 +793,9 @@
public void testOtherDisplayTasks_expectNoTrim() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
- final MyTestActivityStackSupervisor supervisor =
- (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
final ActivityStack homeStack = mDisplay.getHomeStack();
- final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor);
+ final ActivityStack otherDisplayStack = mOtherDisplay.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
// removed
@@ -887,16 +896,16 @@
mStack.remove();
// The following APIs should not restore task from recents to the active list.
- assertNotRestoreTask(() -> mTestService.setFocusedTask(taskId));
- assertNotRestoreTask(() -> mTestService.startSystemLockTaskMode(taskId));
- assertNotRestoreTask(() -> mTestService.cancelTaskWindowTransition(taskId));
+ assertNotRestoreTask(() -> mService.setFocusedTask(taskId));
+ assertNotRestoreTask(() -> mService.startSystemLockTaskMode(taskId));
+ assertNotRestoreTask(() -> mService.cancelTaskWindowTransition(taskId));
assertNotRestoreTask(
- () -> mTestService.resizeTask(taskId, null /* bounds */, 0 /* resizeMode */));
+ () -> mService.resizeTask(taskId, null /* bounds */, 0 /* resizeMode */));
assertNotRestoreTask(
- () -> mTestService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
+ () -> mService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
false/* toTop */));
assertNotRestoreTask(
- () -> mTestService.setTaskWindowingModeSplitScreenPrimary(taskId,
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(taskId,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
false /* toTop */, false /* animate */, null /* initialBounds */,
true /* showRecents */));
@@ -910,7 +919,7 @@
mRecentTasks.remove(task);
TaskChangeNotificationController controller =
- mTestService.getTaskChangeNotificationController();
+ mService.getTaskChangeNotificationController();
verify(controller, times(2)).notifyTaskListUpdated();
}
@@ -923,7 +932,7 @@
// 2 calls - Once for add and once for remove
TaskChangeNotificationController controller =
- mTestService.getTaskChangeNotificationController();
+ mService.getTaskChangeNotificationController();
verify(controller, times(2)).notifyTaskListUpdated();
}
@@ -938,7 +947,7 @@
// 4 calls - Twice for add and twice for remove
TaskChangeNotificationController controller =
- mTestService.getTaskChangeNotificationController();
+ mService.getTaskChangeNotificationController();
verify(controller, times(4)).notifyTaskListUpdated();
}
@@ -980,7 +989,7 @@
@Test
public void testNotRecentsComponent_denyApiAccess() throws Exception {
- doReturn(PackageManager.PERMISSION_DENIED).when(mTestService)
+ doReturn(PackageManager.PERMISSION_DENIED).when(mService)
.checkGetTasksPermission(anyString(), anyInt(), anyInt());
// Expect the following methods to fail due to recents component not being set
mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
@@ -992,7 +1001,7 @@
@Test
public void testRecentsComponent_allowApiAccessWithoutPermissions() {
- doReturn(PackageManager.PERMISSION_DENIED).when(mTestService)
+ doReturn(PackageManager.PERMISSION_DENIED).when(mService)
.checkGetTasksPermission(anyString(), anyInt(), anyInt());
// Set the recents component and ensure that the following calls do not fail
@@ -1002,62 +1011,62 @@
}
private void doTestRecentTasksApis(boolean expectCallable) {
- assertSecurityException(expectCallable, () -> mTestService.removeStack(INVALID_STACK_ID));
+ assertSecurityException(expectCallable, () -> mService.removeStack(INVALID_STACK_ID));
assertSecurityException(expectCallable,
- () -> mTestService.removeStacksInWindowingModes(
+ () -> mService.removeStacksInWindowingModes(
new int[]{WINDOWING_MODE_UNDEFINED}));
assertSecurityException(expectCallable,
- () -> mTestService.removeStacksWithActivityTypes(
+ () -> mService.removeStacksWithActivityTypes(
new int[]{ACTIVITY_TYPE_UNDEFINED}));
- assertSecurityException(expectCallable, () -> mTestService.removeTask(0));
+ assertSecurityException(expectCallable, () -> mService.removeTask(0));
assertSecurityException(expectCallable,
- () -> mTestService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
+ () -> mService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
assertSecurityException(expectCallable,
- () -> mTestService.moveTaskToStack(0, INVALID_STACK_ID, true));
+ () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mTestService.setTaskWindowingModeSplitScreenPrimary(0,
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
- assertSecurityException(expectCallable, () -> mTestService.dismissSplitScreenMode(true));
- assertSecurityException(expectCallable, () -> mTestService.dismissPip(true, 0));
+ assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
+ assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
assertSecurityException(expectCallable,
- () -> mTestService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
+ () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
assertSecurityException(expectCallable,
- () -> mTestService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
+ () -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
assertSecurityException(expectCallable,
- () -> mTestService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
+ () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
new Rect()));
assertSecurityException(expectCallable,
- () -> mTestService.resizePinnedStack(new Rect(), new Rect()));
- assertSecurityException(expectCallable, () -> mTestService.getAllStackInfos());
+ () -> mService.resizePinnedStack(new Rect(), new Rect()));
+ assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
assertSecurityException(expectCallable,
- () -> mTestService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
+ () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
assertSecurityException(expectCallable, () -> {
try {
- mTestService.getFocusedStackInfo();
+ mService.getFocusedStackInfo();
} catch (RemoteException e) {
// Ignore
}
});
assertSecurityException(expectCallable,
- () -> mTestService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
+ () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mTestService.startActivityFromRecents(0, new Bundle()));
- assertSecurityException(expectCallable, () -> mTestService.getTaskSnapshot(0, true));
- assertSecurityException(expectCallable, () -> mTestService.registerTaskStackListener(null));
+ () -> mService.startActivityFromRecents(0, new Bundle()));
+ assertSecurityException(expectCallable, () -> mService.getTaskSnapshot(0, true));
+ assertSecurityException(expectCallable, () -> mService.registerTaskStackListener(null));
assertSecurityException(expectCallable,
- () -> mTestService.unregisterTaskStackListener(null));
- assertSecurityException(expectCallable, () -> mTestService.getTaskDescription(0));
- assertSecurityException(expectCallable, () -> mTestService.cancelTaskWindowTransition(0));
- assertSecurityException(expectCallable, () -> mTestService.startRecentsActivity(null, null,
+ () -> mService.unregisterTaskStackListener(null));
+ assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
+ assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
+ assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
null));
- assertSecurityException(expectCallable, () -> mTestService.cancelRecentsAnimation(true));
- assertSecurityException(expectCallable, () -> mTestService.stopAppSwitches());
- assertSecurityException(expectCallable, () -> mTestService.resumeAppSwitches());
+ assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation(true));
+ assertSecurityException(expectCallable, () -> mService.stopAppSwitches());
+ assertSecurityException(expectCallable, () -> mService.resumeAppSwitches());
}
private void testGetTasksApis(boolean expectCallable) {
- mTestService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
- mTestService.getTasks(MAX_VALUE);
+ mService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
+ mService.getTasks(MAX_VALUE);
if (expectCallable) {
assertTrue(mRecentTasks.mLastAllowed);
assertTrue(mRunningTasks.mLastAllowed);
@@ -1072,10 +1081,9 @@
}
private TaskBuilder createTaskBuilder(String packageName, String className) {
- return new TaskBuilder(mTestService.mStackSupervisor)
+ return new TaskBuilder(mService.mStackSupervisor)
.setComponent(new ComponentName(packageName, className))
.setStack(mStack)
- .setTaskId(sLastTaskId++)
.setUserId(TEST_USER_0_ID);
}
@@ -1140,68 +1148,6 @@
}
}
- private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
- MyTestActivityTaskManagerService(Context context) {
- super(context);
- }
-
- @Override
- protected RecentTasks createRecentTasks() {
- return spy(new TestRecentTasks(this, mTaskPersister));
- }
-
- @Override
- protected ActivityStackSupervisor createStackSupervisor() {
- if (mTestStackSupervisor == null) {
- mTestStackSupervisor = new MyTestActivityStackSupervisor(this, mH.getLooper());
- }
- return mTestStackSupervisor;
- }
-
- @Override
- void createDefaultDisplay() {
- super.createDefaultDisplay();
- mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
- mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
- mSingleTaskDisplay = TestActivityDisplay.create(mTestStackSupervisor,
- DEFAULT_DISPLAY + 2);
- mSingleTaskDisplay.setDisplayToSingleTaskInstance();
- mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
- mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
- mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
- }
- }
-
- private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor {
- MyTestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
- super(service, looper);
- }
-
- @Override
- RunningTasks createRunningTasks() {
- mRunningTasks = new TestRunningTasks();
- return mRunningTasks;
- }
- }
-
- private static class MyTestActivityStack extends TestActivityStack {
- private ActivityDisplay mDisplay = null;
-
- MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) {
- super(display, sLastStackId++, supervisor, WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, true /* onTop */, false /* createActivity */);
- mDisplay = display;
- }
-
- @Override
- ActivityDisplay getDisplay() {
- if (mDisplay != null) {
- return mDisplay;
- }
- return super.getDisplay();
- }
- }
-
private static class CallbacksRecorder implements Callbacks {
public final ArrayList<TaskRecord> mAdded = new ArrayList<>();
public final ArrayList<TaskRecord> mTrimmed = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index f5a1d75..9ca0180 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -30,6 +30,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -132,8 +133,14 @@
@Test
public void testIncludedApps_expectTargetAndVisible() {
mWm.setRecentsAnimationController(mController);
- final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ final ActivityStack homStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final AppWindowToken homeAppWindow =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homStack)
+ .setCreateTask(true)
+ .build()
+ .mAppWindowToken;
final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent,
@@ -169,7 +176,7 @@
// Simulate the app transition finishing
mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0, 0, 0);
- verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
+ verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
}
@Test
@@ -201,7 +208,7 @@
spyOn(mController.mRecentScreenshotAnimator.mAnimatable);
mController.mRecentScreenshotAnimator.cancelAnimation();
verify(mController.mRecentScreenshotAnimator.mAnimatable).onAnimationLeashLost(any());
- verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
+ verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 0e119e3..dcc295c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -74,8 +74,9 @@
@Before
public void setUp() throws Exception {
mRecentsAnimationController = mock(RecentsAnimationController.class);
- doReturn(mRecentsAnimationController).when(
- mService.mWindowManager).getRecentsAnimationController();
+ mService.mWindowManager.setRecentsAnimationController(mRecentsAnimationController);
+ doNothing().when(mService.mWindowManager).initializeRecentsAnimation(
+ anyInt(), any(), any(), anyInt(), any());
doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
final RecentTasks recentTasks = mService.getRecentTasks();
@@ -107,16 +108,25 @@
assertTrue(recentActivity.visible);
// Simulate the animation is cancelled without changing the stack order.
- recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */,
- false /* sendUserLeaveHint */);
+ recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
// The non-top recents activity should be invisible by the restored launch-behind state.
assertFalse(recentActivity.visible);
}
@Test
public void testPreloadRecentsActivity() {
- // Ensure that the fake recent component can be resolved by the recents intent.
- mockTaskRecordFactory(builder -> builder.setComponent(mRecentsComponent));
+ final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
+ final ActivityStack homeStack =
+ defaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ defaultDisplay.positionChildAtTop(homeStack, false /* includingParents */);
+ ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+ if (topRunningHomeActivity == null) {
+ topRunningHomeActivity = new ActivityBuilder(mService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ }
+
ActivityInfo aInfo = new ActivityInfo();
aInfo.applicationInfo = new ApplicationInfo();
aInfo.applicationInfo.uid = 10001;
@@ -204,6 +214,13 @@
ActivityStack homeStack = display.getHomeStack();
// Assume the home activity support recents.
ActivityRecord targetActivity = homeStack.getTopActivity();
+ if (targetActivity == null) {
+ targetActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setStack(homeStack)
+ .build();
+ }
+
// Put another home activity in home stack.
ActivityRecord anotherHomeActivity = new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "Home2"))
@@ -226,13 +243,12 @@
anotherHomeActivity.moveFocusableActivityToTop("launchAnotherHome");
// The current top activity is not the recents so the animation should be canceled.
- verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+ verify(mService.mWindowManager, times(1)).cancelRecentsAnimation(
eq(REORDER_KEEP_IN_PLACE), any() /* reason */);
// The test uses mocked RecentsAnimationController so we have to invoke the callback
// manually to simulate the flow.
- recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */,
- false /* sendUserLeaveHint */);
+ recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
// We should restore the launch-behind of the original target activity.
assertFalse(targetActivity.mLaunchTaskBehind);
}
@@ -269,7 +285,7 @@
fullscreenStack.moveToFront("Activity start");
// Ensure that the recents animation was canceled by cancelAnimationSynchronously().
- verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+ verify(mService.mWindowManager, times(1)).cancelRecentsAnimation(
eq(REORDER_KEEP_IN_PLACE), any());
// Assume recents animation already started, set a state that cancel recents animation
@@ -314,7 +330,7 @@
fullscreenStack.remove();
// Ensure that the recents animation was NOT canceled
- verify(mService.mWindowManager, times(0)).cancelRecentsAnimationSynchronously(
+ verify(mService.mWindowManager, times(0)).cancelRecentsAnimation(
eq(REORDER_KEEP_IN_PLACE), any());
verify(mRecentsAnimationController, times(0)).setCancelOnNextTransitionStart();
}
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 d4f24f9..539a79c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -97,7 +97,7 @@
*/
@Test
public void testRestoringInvalidTask() {
- ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks();
+ mRootActivityContainer.getDefaultDisplay().removeAllTasks();
TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
assertNull(task);
@@ -304,21 +304,23 @@
*/
@Test
public void testResizeDockedStackForSplitScreenPrimary() {
- final Rect taskSize = new Rect(0, 0, 600, 600);
+ final Rect taskSize = new Rect(0, 0, 1000, 1000);
final Rect stackSize = new Rect(0, 0, 300, 300);
// Create primary split-screen stack with a task.
- final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
- .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+ final ActivityStack primaryStack = new StackBuilder(mRootActivityContainer)
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+ .setOnTop(true)
+ .build();
+ final TaskRecord task = primaryStack.topTask();
// Resize dock stack.
mService.resizeDockedStack(stackSize, taskSize, null, null, null);
// Verify dock stack & its task bounds if is equal as resized result.
- assertEquals(primaryStack.getBounds(), stackSize);
- assertEquals(task.getBounds(), taskSize);
+ assertEquals(stackSize, primaryStack.getBounds());
+ assertEquals(taskSize, task.getBounds());
}
/**
@@ -328,8 +330,9 @@
public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
// Create stack/task on default display.
final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
- final TestActivityStack targetStack = (TestActivityStack) new StackBuilder(
- mRootActivityContainer).setOnTop(false).build();
+ final ActivityStack targetStack = new StackBuilder(mRootActivityContainer)
+ .setOnTop(false)
+ .build();
final TaskRecord targetTask = targetStack.getChildAt(0);
// Create Recents on top of the display.
@@ -505,12 +508,10 @@
mockResolveSecondaryHomeActivity();
// Create secondary displays.
- final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+ final TestActivityDisplay secondDisplay = createNewActivityDisplay();
mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
doReturn(true).when(secondDisplay).supportsSystemDecorations();
- // Create mock tasks and other necessary mocks.
- mockTaskRecordFactory();
doReturn(true).when(mRootActivityContainer)
.ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
@@ -621,7 +622,6 @@
info.applicationInfo.packageName = "android";
info.name = ResolverActivity.class.getName();
doReturn(info).when(mRootActivityContainer).resolveHomeActivity(anyInt(), any());
- mockTaskRecordFactory();
mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index f51ce13..db105dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -96,7 +96,7 @@
mWm.getDefaultDisplayContentLocked().getWindowingMode());
mWm.mIsPc = true;
- mWm.mSupportsFreeformWindowManagement = true;
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.mRoot.onSettingsRetrieved();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index df7c9a4..1ad0e00 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.os.Process.THREAD_PRIORITY_DEFAULT;
import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -25,67 +28,91 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
+import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.net.Uri;
import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.UserHandle;
-import android.view.Display;
+import android.provider.DeviceConfig;
import android.view.InputChannel;
+import android.view.Surface;
+import android.view.SurfaceControl;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.AnimationThread;
+import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
+import com.android.server.ServiceThread;
import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.appop.AppOpsService;
+import com.android.server.display.color.ColorDisplayService;
import com.android.server.input.InputManagerService;
+import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wm.utils.MockTracker;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.uri.UriGrantsManagerInternal;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
-import org.mockito.invocation.InvocationOnMock;
+import org.mockito.Mockito;
import org.mockito.quality.Strictness;
+import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * JUnit test rule to create a mock {@link WindowManagerService} instance for tests.
+ * JUnit test rule to correctly setting up system services like {@link WindowManagerService}
+ * and {@link ActivityTaskManagerService} for tests.
*/
public class SystemServicesTestRule implements TestRule {
private static final String TAG = SystemServicesTestRule.class.getSimpleName();
+ static int sNextDisplayId = DEFAULT_DISPLAY + 100;
+ static int sNextTaskId = 100;
+
private final AtomicBoolean mCurrentMessagesProcessed = new AtomicBoolean(false);
- private MockTracker mMockTracker;
+ private Context mContext;
private StaticMockitoSession mMockitoSession;
- private WindowManagerService mWindowManagerService;
- private TestWindowManagerPolicy mWindowManagerPolicy;
-
- /** {@link MockTracker} to track mocks created by {@link SystemServicesTestRule}. */
- private static class Tracker extends MockTracker {
- // This empty extended class is necessary since Mockito distinguishes a listener by it
- // class.
- }
+ ServiceThread mHandlerThread;
+ private ActivityManagerService mAmService;
+ private ActivityTaskManagerService mAtmService;
+ private WindowManagerService mWmService;
+ private TestWindowManagerPolicy mWMPolicy;
+ private WindowState.PowerManagerWrapper mPowerManagerWrapper;
+ private InputManagerService mImService;
+ /**
+ * Spied {@link SurfaceControl.Transaction} class than can be used to verify calls.
+ */
+ SurfaceControl.Transaction mTransaction;
@Override
public Statement apply(Statement base, Description description) {
@@ -103,8 +130,6 @@
}
private void setUp() {
- mMockTracker = new Tracker();
-
mMockitoSession = mockitoSession()
.spyStatic(LocalServices.class)
.mockStatic(LockGuard.class)
@@ -112,101 +137,220 @@
.strictness(Strictness.LENIENT)
.startMocking();
+ setUpSystemCore();
+ setUpLocalServices();
+ setUpActivityTaskManagerService();
+ setUpWindowManagerService();
+ }
+
+ private void setUpSystemCore() {
+ mHandlerThread = new ServiceThread(
+ "WmTestsThread", THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+ mHandlerThread.start();
+
doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);
- final Context context = getInstrumentation().getTargetContext();
- spyOn(context);
+ mContext = getInstrumentation().getTargetContext();
+ spyOn(mContext);
- doReturn(null).when(context)
+ doReturn(null).when(mContext)
.registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class));
- doReturn(null).when(context)
+ doReturn(null).when(mContext)
.registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class),
any(IntentFilter.class), nullable(String.class), nullable(Handler.class));
- final ContentResolver contentResolver = context.getContentResolver();
+ final ContentResolver contentResolver = mContext.getContentResolver();
spyOn(contentResolver);
doNothing().when(contentResolver)
.registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
anyInt());
+ }
- final AppOpsManager appOpsManager = mock(AppOpsManager.class);
- doReturn(appOpsManager).when(context)
- .getSystemService(eq(Context.APP_OPS_SERVICE));
+ private void setUpLocalServices() {
+ // Tear down any local services just in case.
+ tearDownLocalServices();
+ // UriGrantsManagerInternal
+ final UriGrantsManagerInternal ugmi = mock(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugmi);
+
+ // AppOpsManager
+ final AppOpsManager aom = mock(AppOpsManager.class);
+ doReturn(aom).when(mContext).getSystemService(eq(Context.APP_OPS_SERVICE));
+
+ // DisplayManagerInternal
final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
doReturn(dmi).when(() -> LocalServices.getService(eq(DisplayManagerInternal.class)));
+ // ColorDisplayServiceInternal
+ final ColorDisplayService.ColorDisplayServiceInternal cds =
+ mock(ColorDisplayService.ColorDisplayServiceInternal.class);
+ doReturn(cds).when(() -> LocalServices.getService(
+ eq(ColorDisplayService.ColorDisplayServiceInternal.class)));
+
+ final UsageStatsManagerInternal usmi = mock(UsageStatsManagerInternal.class);
+ LocalServices.addService(UsageStatsManagerInternal.class, usmi);
+
+ // PackageManagerInternal
+ final PackageManagerInternal packageManagerInternal = mock(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, packageManagerInternal);
+ doReturn(false).when(packageManagerInternal).isPermissionsReviewRequired(
+ anyString(), anyInt());
+ doReturn(null).when(packageManagerInternal).getDefaultHomeActivity(anyInt());
+
+ // PowerManagerInternal
final PowerManagerInternal pmi = mock(PowerManagerInternal.class);
final PowerSaveState state = new PowerSaveState.Builder().build();
doReturn(state).when(pmi).getLowPowerState(anyInt());
doReturn(pmi).when(() -> LocalServices.getService(eq(PowerManagerInternal.class)));
- final ActivityManagerInternal ami = mock(ActivityManagerInternal.class);
- doReturn(ami).when(() -> LocalServices.getService(eq(ActivityManagerInternal.class)));
+ // PermissionPolicyInternal
+ final PermissionPolicyInternal ppi = mock(PermissionPolicyInternal.class);
+ LocalServices.addService(PermissionPolicyInternal.class, ppi);
+ doReturn(true).when(ppi).checkStartActivity(any(), anyInt(), any());
- final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class);
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final Runnable runnable = invocationOnMock.getArgument(0);
- if (runnable != null) {
- runnable.run();
- }
- return null;
- }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt());
- doReturn(atmi).when(() -> LocalServices.getService(eq(ActivityTaskManagerInternal.class)));
-
- final InputManagerService ims = mock(InputManagerService.class);
+ // InputManagerService
+ mImService = mock(InputManagerService.class);
// InputChannel is final and can't be mocked.
final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
if (input != null && input.length > 1) {
- doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
+ doReturn(input[1]).when(mImService).monitorInput(anyString(), anyInt());
}
- final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
- final TaskChangeNotificationController taskChangeNotificationController = mock(
- TaskChangeNotificationController.class);
- doReturn(taskChangeNotificationController).when(atms).getTaskChangeNotificationController();
- final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
- doReturn(wmLock).when(atms).getGlobalLock();
+ // StatusBarManagerInternal
+ final StatusBarManagerInternal sbmi = mock(StatusBarManagerInternal.class);
+ doReturn(sbmi).when(() -> LocalServices.getService(eq(StatusBarManagerInternal.class)));
+ }
- mWindowManagerPolicy = new TestWindowManagerPolicy(this::getWindowManagerService);
- mWindowManagerService = WindowManagerService.main(
- context, ims, false, false, mWindowManagerPolicy, atms, StubTransaction::new);
+ private void setUpActivityTaskManagerService() {
+ // ActivityManagerService
+ mAmService = new ActivityManagerService(
+ new AMTestInjector(mContext, mHandlerThread), mHandlerThread);
+ spyOn(mAmService);
+ doReturn(mock(IPackageManager.class)).when(mAmService).getPackageManager();
+ doNothing().when(mAmService).grantEphemeralAccessLocked(
+ anyInt(), any(), anyInt(), anyInt());
- mWindowManagerService.onInitReady();
+ // ActivityManagerInternal
+ final ActivityManagerInternal amInternal = mAmService.mInternal;
+ spyOn(amInternal);
+ doNothing().when(amInternal).trimApplications();
+ doNothing().when(amInternal).updateCpuStats();
+ doNothing().when(amInternal).updateOomAdj();
+ doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean());
+ doNothing().when(amInternal).updateActivityUsageStats(
+ any(), anyInt(), anyInt(), any(), any());
+ doNothing().when(amInternal).startProcess(
+ any(), any(), anyBoolean(), anyBoolean(), any(), any());
+ doNothing().when(amInternal).updateOomLevelsForDisplay(anyInt());
+ LocalServices.addService(ActivityManagerInternal.class, amInternal);
- final Display display = mWindowManagerService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
- // Display creation is driven by the ActivityManagerService via
- // ActivityStackSupervisor. We emulate those steps here.
- DisplayContent displayContent = mWindowManagerService.mRoot
- .createDisplayContent(display, mock(ActivityDisplay.class));
- displayContent.reconfigureDisplayLocked();
+ mAtmService = new TestActivityTaskManagerService(mContext, mAmService);
+ LocalServices.addService(ActivityTaskManagerInternal.class, mAtmService.getAtmInternal());
+ }
- mMockTracker.stopTracking();
+ private void setUpWindowManagerService() {
+ mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
+ mWMPolicy = new TestWindowManagerPolicy(this::getWindowManagerService,
+ mPowerManagerWrapper);
+ mWmService = WindowManagerService.main(
+ mContext, mImService, false, false, mWMPolicy, mAtmService, StubTransaction::new);
+ spyOn(mWmService);
+
+ // Setup factory classes to prevent calls to native code.
+ mTransaction = spy(StubTransaction.class);
+ // Return a spied Transaction class than can be used to verify calls.
+ mWmService.mTransactionFactory = () -> mTransaction;
+ // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances.
+ mWmService.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder();
+ // Return mocked Surface instances.
+ mWmService.mSurfaceFactory = () -> mock(Surface.class);
+ mWmService.mSurfaceAnimationRunner = new SurfaceAnimationRunner(
+ null, null, mTransaction, mWmService.mPowerManagerInternal);
+
+ mWmService.onInitReady();
+ mAmService.setWindowManager(mWmService);
+ mWmService.mDisplayEnabled = true;
+ mWmService.mDisplayReady = true;
+ // Set configuration for default display
+ mWmService.getDefaultDisplayContentLocked().reconfigureDisplayLocked();
+
+ // Mock root, some default display, and home stack.
+ spyOn(mWmService.mRoot);
+ final ActivityDisplay display = mAtmService.mRootActivityContainer.getDefaultDisplay();
+ spyOn(display);
+ spyOn(display.mDisplayContent);
+ final ActivityStack homeStack = display.getStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ spyOn(homeStack);
+ spyOn(homeStack.mTaskStack);
}
private void tearDown() {
waitUntilWindowManagerHandlersIdle();
- removeLocalServices();
- mWindowManagerService = null;
- mWindowManagerPolicy = null;
+ // Unregister display listener from root to avoid issues with subsequent tests.
+ mContext.getSystemService(DisplayManager.class)
+ .unregisterDisplayListener(mAtmService.mRootActivityContainer);
+ // ProptertiesChangesListener is registered in the constructor of WindowManagerService to
+ // a static object, so we need to clean it up in tearDown(), even though we didn't set up
+ // in tests.
+ DeviceConfig.removeOnPropertiesChangedListener(mWmService.mPropertiesChangedListener);
+ mWmService = null;
+ mWMPolicy = null;
+ mPowerManagerWrapper = null;
+
+ tearDownLocalServices();
+ tearDownSystemCore();
+
+ // Needs to explicitly dispose current static threads because there could be messages
+ // scheduled at a later time, and all mocks are invalid when it's executed.
+ DisplayThread.dispose();
+ AnimationThread.dispose();
+ // Reset priority booster because animation thread has been changed.
+ WindowManagerService.sThreadPriorityBooster = new WindowManagerThreadPriorityBooster();
+
+ Mockito.framework().clearInlineMocks();
+ }
+
+ private void tearDownSystemCore() {
if (mMockitoSession != null) {
mMockitoSession.finishMocking();
mMockitoSession = null;
}
- if (mMockTracker != null) {
- mMockTracker.close();
- mMockTracker = null;
+ if (mHandlerThread != null) {
+ // Make sure there are no running messages and then quit the thread so the next test
+ // won't be affected.
+ mHandlerThread.getThreadHandler().runWithScissors(mHandlerThread::quit,
+ 0 /* timeout */);
}
}
- private static void removeLocalServices() {
+ private static void tearDownLocalServices() {
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.removeServiceForTest(PermissionPolicyInternal.class);
+ LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
+ LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
}
WindowManagerService getWindowManagerService() {
- return mWindowManagerService;
+ return mWmService;
+ }
+
+ ActivityTaskManagerService getActivityTaskManagerService() {
+ return mAtmService;
+ }
+
+ WindowState.PowerManagerWrapper getPowerManagerWrapper() {
+ return mPowerManagerWrapper;
}
void cleanupWindowManagerHandlers() {
@@ -229,6 +373,7 @@
waitHandlerIdle(wm.mH);
waitHandlerIdle(wm.mAnimationHandler);
waitHandlerIdle(SurfaceAnimationThread.getHandler());
+ waitHandlerIdle(mHandlerThread.getThreadHandler());
}
private void waitHandlerIdle(Handler handler) {
@@ -251,4 +396,121 @@
}
}
}
+
+ protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
+ // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
+ // We keep the reference in order to prevent creating it twice.
+ ActivityStackSupervisor mTestStackSupervisor;
+
+ TestActivityTaskManagerService(Context context, ActivityManagerService ams) {
+ super(context);
+ spyOn(this);
+
+ mSupportsMultiWindow = true;
+ mSupportsMultiDisplay = true;
+ mSupportsSplitScreenMultiWindow = true;
+ mSupportsFreeformWindowManagement = true;
+ mSupportsPictureInPicture = true;
+
+ doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+ // allow background activity starts by default
+ doReturn(true).when(this).isBackgroundActivityStartsEnabled();
+ doNothing().when(this).updateCpuStats();
+
+ // AppOpsService
+ final AppOpsService aos = mock(AppOpsService.class);
+ doReturn(aos).when(this).getAppOpsService();
+ // Make sure permission checks aren't overridden.
+ doReturn(AppOpsManager.MODE_DEFAULT)
+ .when(aos).noteOperation(anyInt(), anyInt(), anyString());
+
+ setUsageStatsManager(LocalServices.getService(UsageStatsManagerInternal.class));
+ ams.mActivityTaskManager = this;
+ ams.mAtmInternal = mInternal;
+ onActivityManagerInternalAdded();
+ initialize(
+ ams.mIntentFirewall, ams.mPendingIntentController, mHandlerThread.getLooper());
+ spyOn(getLifecycleManager());
+ spyOn(getLockTaskController());
+ spyOn(getTaskChangeNotificationController());
+ initRootActivityContainerMocks();
+ }
+
+ void initRootActivityContainerMocks() {
+ spyOn(mRootActivityContainer);
+ // Invoked during {@link ActivityStack} creation.
+ doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
+ // Always keep things awake.
+ doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
+ // Called when moving activity to pinned stack.
+ doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
+ anyBoolean());
+ }
+
+ @Override
+ int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+ return userId;
+ }
+
+ @Override
+ protected ActivityStackSupervisor createStackSupervisor() {
+ if (mTestStackSupervisor == null) {
+ mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
+ }
+ return mTestStackSupervisor;
+ }
+ }
+
+ /**
+ * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
+ * setup not available in the test environment. Also specifies an injector for
+ */
+ protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
+
+ TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+ super(service, looper);
+ spyOn(this);
+
+ // Do not schedule idle that may touch methods outside the scope of the test.
+ doNothing().when(this).scheduleIdleLocked();
+ doNothing().when(this).scheduleIdleTimeoutLocked(any());
+ // unit test version does not handle launch wake lock
+ doNothing().when(this).acquireLaunchWakelock();
+ doReturn(mock(KeyguardController.class)).when(this).getKeyguardController();
+
+ mLaunchingActivityWakeLock = mock(PowerManager.WakeLock.class);
+
+ initialize();
+ }
+ }
+
+ // TODO: Can we just mock this?
+ private static class AMTestInjector extends ActivityManagerService.Injector {
+ private ServiceThread mHandlerThread;
+
+ AMTestInjector(Context context, ServiceThread handlerThread) {
+ super(context);
+ mHandlerThread = handlerThread;
+ }
+
+ @Override
+ public Context getContext() {
+ return getInstrumentation().getTargetContext();
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return null;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandlerThread.getThreadHandler();
+ }
+
+ @Override
+ public boolean isNetworkRestrictedForUid(int uid) {
+ return false;
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 2cebebf..bcff704 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1277,17 +1277,18 @@
}
private ActivityRecord createSourceActivity(TestActivityDisplay display) {
- final TestActivityStack stack = display.createStack(display.getWindowingMode(),
+ final ActivityStack stack = display.createStack(display.getWindowingMode(),
ACTIVITY_TYPE_STANDARD, true);
return new ActivityBuilder(mService).setStack(stack).setCreateTask(true).build();
}
private void addFreeformTaskTo(TestActivityDisplay display, Rect bounds) {
- final TestActivityStack stack = display.createStack(display.getWindowingMode(),
+ final ActivityStack stack = display.createStack(display.getWindowingMode(),
ACTIVITY_TYPE_STANDARD, true);
stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
- task.setBounds(bounds);
+ // Just work around the unnecessary adjustments for bounds.
+ task.getWindowConfiguration().setBounds(bounds);
}
private void assertEquivalentWindowingMode(int expected, int actual, int parentWindowingMode) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index a0302f6..c83401b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -28,14 +28,19 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals;
@@ -64,6 +69,7 @@
import android.util.Xml;
import android.view.DisplayInfo;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import com.android.internal.app.IVoiceInteractor;
@@ -102,6 +108,7 @@
public void setUp() throws Exception {
TaskRecord.setTaskRecordFactory(null);
mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
+ removeGlobalMinSizeRestriction();
}
@Test
@@ -165,6 +172,7 @@
/** Ensures that bounds on freeform stacks are not clipped. */
@Test
+ @FlakyTest(bugId = 137879065)
public void testAppBounds_FreeFormBounds() {
final Rect freeFormBounds = new Rect(mParentBounds);
freeFormBounds.offset(10, 10);
@@ -174,6 +182,7 @@
/** Ensures that fully contained bounds are not clipped. */
@Test
+ @FlakyTest(bugId = 137879065)
public void testAppBounds_ContainedBounds() {
final Rect insetBounds = new Rect(mParentBounds);
insetBounds.inset(5, 5, 5, 5);
@@ -182,6 +191,7 @@
}
@Test
+ @FlakyTest(bugId = 137879065)
public void testFitWithinBounds() {
final Rect parentBounds = new Rect(10, 10, 200, 200);
ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
@@ -221,6 +231,7 @@
/** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
@Test
+ @FlakyTest(bugId = 137879065)
public void testBoundsOnModeChangeFreeformToFullscreen() {
ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display)
@@ -248,18 +259,6 @@
}
/**
- * This is a temporary hack to trigger an onConfigurationChange at the task level after an
- * orientation is requested. Normally this is done by the onDescendentOrientationChanged call
- * up the WM hierarchy, but since the WM hierarchy is mocked out, it doesn't happen here.
- * TODO: remove this when we either get a WM hierarchy or when hierarchies are merged.
- */
- private void setActivityRequestedOrientation(ActivityRecord activity, int orientation) {
- activity.setRequestedOrientation(orientation);
- ConfigurationContainer taskRecord = activity.getParent();
- taskRecord.onConfigurationChanged(taskRecord.getParent().getConfiguration());
- }
-
- /**
* Tests that a task with forced orientation has orientation-consistent bounds within the
* parent.
*/
@@ -268,49 +267,48 @@
final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
DisplayInfo info = new DisplayInfo();
+ mService.mContext.getDisplay().getDisplayInfo(info);
info.logicalWidth = fullScreenBounds.width();
info.logicalHeight = fullScreenBounds.height();
ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null);
- // Override display orientation. Normally this is available via DisplayContent, but DC
- // is mocked-out.
- display.getRequestedOverrideConfiguration().orientation =
- Configuration.ORIENTATION_LANDSCAPE;
- display.onRequestedOverrideConfigurationChanged(
- display.getRequestedOverrideConfiguration());
+ // Fix the display orientation to landscape which is the natural rotation (0) for the test
+ // display.
+ final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
+ dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+
ActivityStack stack = new StackBuilder(mRootActivityContainer)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
TaskRecord task = stack.getChildAt(0);
ActivityRecord root = task.getTopActivity();
- assertEquals(root, task.getTopActivity());
assertEquals(fullScreenBounds, task.getBounds());
// Setting app to fixed portrait fits within parent
- setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+ root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(root, task.getRootActivity());
assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
- assertTrue(task.getBounds().width() < task.getBounds().height());
+ assertThat(task.getBounds().width()).isLessThan(task.getBounds().height());
assertEquals(fullScreenBounds.height(), task.getBounds().height());
// Top activity gets used
ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
assertEquals(top, task.getTopActivity());
- setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
- assertTrue(task.getBounds().width() > task.getBounds().height());
+ top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
assertEquals(task.getBounds().width(), fullScreenBounds.width());
// Setting app to unspecified restores
- setActivityRequestedOrientation(top, SCREEN_ORIENTATION_UNSPECIFIED);
+ top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
assertEquals(fullScreenBounds, task.getBounds());
// Setting app to fixed landscape and changing display
- setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
- // simulate display orientation changing (normally done via DisplayContent)
- display.getRequestedOverrideConfiguration().orientation =
- Configuration.ORIENTATION_PORTRAIT;
- display.setBounds(fullScreenBoundsPort);
- assertTrue(task.getBounds().width() > task.getBounds().height());
+ top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ // Fix the display orientation to portrait which is 90 degrees for the test display.
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
+
+ assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
// in FREEFORM, no constraint
@@ -323,7 +321,7 @@
// FULLSCREEN letterboxes bounds
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertTrue(task.getBounds().width() > task.getBounds().height());
+ assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
// FREEFORM restores bounds as before
@@ -335,6 +333,7 @@
public void testIgnoresForcedOrientationWhenParentHandles() {
final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
DisplayInfo info = new DisplayInfo();
+ mService.mContext.getDisplay().getDisplayInfo(info);
info.logicalWidth = fullScreenBounds.width();
info.logicalHeight = fullScreenBounds.height();
ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
@@ -355,7 +354,7 @@
// Setting app to fixed portrait fits within parent, but TaskRecord shouldn't adjust the
// bounds because its parent says it will handle it at a later time.
- setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+ root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(root, task.getRootActivity());
assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
assertEquals(fullScreenBounds, task.getBounds());
@@ -646,7 +645,7 @@
final ActivityRecord activity0 = task0.getChildAt(0);
final TaskRecord task1 = getTestTask();
- final ActivityRecord activity1 = task0.getChildAt(0);
+ final ActivityRecord activity1 = task1.getChildAt(0);
assertEquals(task0.taskId,
ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
new file mode 100644
index 0000000..c143969
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 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.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import android.hardware.display.DisplayManagerGlobal;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+class TestActivityDisplay extends ActivityDisplay {
+ private final ActivityStackSupervisor mSupervisor;
+
+ static TestActivityDisplay create(ActivityStackSupervisor supervisor) {
+ return create(supervisor, SystemServicesTestRule.sNextDisplayId++);
+ }
+
+ static TestActivityDisplay create(ActivityStackSupervisor supervisor, DisplayInfo info) {
+ return create(supervisor, SystemServicesTestRule.sNextDisplayId++, info);
+ }
+
+ static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
+ final DisplayInfo info = new DisplayInfo();
+ supervisor.mService.mContext.getDisplay().getDisplayInfo(info);
+ return create(supervisor, displayId, info);
+ }
+
+ static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId,
+ DisplayInfo info) {
+ if (displayId == DEFAULT_DISPLAY) {
+ synchronized (supervisor.mService.mGlobalLock) {
+ return new TestActivityDisplay(supervisor,
+ supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
+ }
+ }
+ final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+ info, DEFAULT_DISPLAY_ADJUSTMENTS);
+
+ synchronized (supervisor.mService.mGlobalLock) {
+ return new TestActivityDisplay(supervisor, display);
+ }
+ }
+
+ private TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
+ super(supervisor.mService.mRootActivityContainer, display);
+ // Normally this comes from display-properties as exposed by WM. Without that, just
+ // hard-code to FULLSCREEN for tests.
+ setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mSupervisor = supervisor;
+ spyOn(this);
+ spyOn(mDisplayContent);
+ doAnswer(invocation -> {
+ // Bypass all the rotation animation and display freezing stuff for testing and just
+ // set the rotation we want for the display
+ final DisplayContent dc = mDisplayContent;
+ final int oldRotation = dc.getRotation();
+ final int rotation = dc.getDisplayRotation().rotationForOrientation(
+ dc.getLastOrientation(), oldRotation);
+ if (oldRotation == rotation) {
+ return false;
+ }
+ dc.setLayoutNeeded();
+ dc.setRotation(rotation);
+ return true;
+ }).when(mDisplayContent).updateRotationUnchecked(anyBoolean());
+ }
+
+ @SuppressWarnings("TypeParameterUnusedInFormals")
+ @Override
+ ActivityStack createStackUnchecked(int windowingMode, int activityType,
+ int stackId, boolean onTop) {
+ return new ActivityTestsBase.StackBuilder(mSupervisor.mRootActivityContainer)
+ .setDisplay(this)
+ .setWindowingMode(windowingMode)
+ .setActivityType(activityType)
+ .setStackId(stackId)
+ .setOnTop(onTop)
+ .setCreateActivity(false)
+ .build();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 2e5ce69..bb89446 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -40,12 +40,14 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.WindowState.PowerManagerWrapper;
import java.io.PrintWriter;
import java.util.function.Supplier;
class TestWindowManagerPolicy implements WindowManagerPolicy {
private final Supplier<WindowManagerService> mWmSupplier;
+ private final PowerManagerWrapper mPowerManagerWrapper;
int mRotationToReport = 0;
boolean mKeyguardShowingAndNotOccluded = false;
@@ -53,8 +55,10 @@
private Runnable mRunnableWhenAddingSplashScreen;
- TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier) {
+ TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier,
+ PowerManagerWrapper powerManagerWrapper) {
mWmSupplier = wmSupplier;
+ mPowerManagerWrapper = powerManagerWrapper;
}
@Override
@@ -121,7 +125,7 @@
doReturn(mock(IBinder.class)).when(iWindow).asBinder();
window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken,
"Starting window", 0 /* ownerId */, false /* internalWindows */, wm,
- mock(Session.class), iWindow);
+ mock(Session.class), iWindow, mPowerManagerWrapper);
atoken.startingWindow = window;
}
if (mRunnableWhenAddingSplashScreen != null) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index acfc2ea..921f105 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -27,6 +27,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -113,6 +114,7 @@
@Test
public void testAddChildSetsSurfacePosition() {
+ reset(mTransaction);
try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) {
WindowContainer child = new WindowContainer(mWm);
child.setBounds(1, 1, 10, 10);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 06bcdf8..60cefe8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -57,6 +57,7 @@
private final IWindow mIWindow = new TestIWindow();
private final Rect mEmptyRect = new Rect();
+ private DisplayContent mTestDisplayContent;
static class FrameTestWindowState extends WindowState {
boolean mDockedResizingForTest = false;
@@ -77,6 +78,9 @@
@Before
public void setUp() throws Exception {
mStubStack = mock(TaskStack.class);
+ DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo);
+ testDisplayInfo.displayCutout = null;
+ mTestDisplayContent = createNewDisplay(testDisplayInfo);
}
// Do not use this function directly in the tests below. Instead, use more explicit function
@@ -100,6 +104,10 @@
assertRect(w.getStableInsets(), left, top, right, bottom);
}
+ private void assertFrame(WindowState w, Rect frame) {
+ assertEquals(w.getFrameLw(), frame);
+ }
+
private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
assertRect(w.getFrameLw(), left, top, right, bottom);
}
@@ -380,9 +388,10 @@
}
@Test
+ @FlakyTest(bugId = 137879065)
public void testLayoutLetterboxedWindow() {
// First verify task behavior in multi-window mode.
- final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo();
+ final DisplayInfo displayInfo = mTestDisplayContent.getDisplayInfo();
final int logicalWidth = displayInfo.logicalWidth;
final int logicalHeight = displayInfo.logicalHeight;
@@ -413,13 +422,14 @@
final Rect cf = new Rect(xInset, 0, logicalWidth - xInset, logicalHeight);
Configuration config = new Configuration(w.mAppToken.getRequestedOverrideConfiguration());
config.windowConfiguration.setBounds(cf);
+ config.windowConfiguration.setAppBounds(cf);
w.mAppToken.onRequestedOverrideConfigurationChanged(config);
pf.set(0, 0, logicalWidth, logicalHeight);
task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.setBounds(null);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw();
- assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
+ assertFrame(w, cf);
assertContentFrame(w, cf);
assertContentInset(w, 0, 0, 0, 0);
}
@@ -483,7 +493,7 @@
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
task.setWindowingMode(WINDOWING_MODE_FREEFORM);
- DisplayContent dc = mWm.getDefaultDisplayContentLocked();
+ DisplayContent dc = mTestDisplayContent;
dc.mInputMethodTarget = w;
WindowState mockIme = mock(WindowState.class);
Mockito.doReturn(true).when(mockIme).isVisibleNow();
@@ -537,7 +547,7 @@
attrs.width = width;
attrs.height = height;
- AppWindowToken token = createAppWindowToken(mWm.getDefaultDisplayContentLocked(),
+ AppWindowToken token = createAppWindowToken(mTestDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
FrameTestWindowState ws = new FrameTestWindowState(mWm, mIWindow, token, attrs);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 36698ea..b731628 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -346,24 +346,28 @@
firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
- reset(sPowerManagerWrapper);
+ final WindowState.PowerManagerWrapper powerManagerWrapper =
+ mSystemServicesTestRule.getPowerManagerWrapper();
+ reset(powerManagerWrapper);
firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
- reset(sPowerManagerWrapper);
+ reset(powerManagerWrapper);
secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
}
private void testPrepareWindowToDisplayDuringRelayout(WindowState appWindow,
boolean expectedWakeupCalled, boolean expectedCurrentLaunchCanTurnScreenOn) {
- reset(sPowerManagerWrapper);
+ final WindowState.PowerManagerWrapper powerManagerWrapper =
+ mSystemServicesTestRule.getPowerManagerWrapper();
+ reset(powerManagerWrapper);
appWindow.prepareWindowToDisplayDuringRelayout(false /* wasVisible */);
if (expectedWakeupCalled) {
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
} else {
- verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
}
// If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false
// because the state will be consumed.
@@ -517,13 +521,19 @@
@Test
public void testGetTransformationMatrix() {
+ final int PARENT_WINDOW_OFFSET = 1;
+ final int DISPLAY_IN_PARENT_WINDOW_OFFSET = 2;
+ final int WINDOW_OFFSET = 3;
+ final float OFFSET_SUM =
+ PARENT_WINDOW_OFFSET + DISPLAY_IN_PARENT_WINDOW_OFFSET + WINDOW_OFFSET;
+
synchronized (mWm.mGlobalLock) {
final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
- win0.getFrameLw().offsetTo(1, 0);
final DisplayContent dc = createNewDisplay();
+ win0.getFrameLw().offsetTo(PARENT_WINDOW_OFFSET, 0);
dc.reparentDisplayContent(win0, win0.getSurfaceControl());
- dc.updateLocation(win0, 2, 0);
+ dc.updateLocation(win0, DISPLAY_IN_PARENT_WINDOW_OFFSET, 0);
final float[] values = new float[9];
final Matrix matrix = new Matrix();
@@ -531,12 +541,12 @@
final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1");
win1.mHasSurface = true;
win1.mSurfaceControl = mock(SurfaceControl.class);
- win1.getFrameLw().offsetTo(3, 0);
+ win1.getFrameLw().offsetTo(WINDOW_OFFSET, 0);
win1.updateSurfacePosition(t);
win1.getTransformationMatrix(values, matrix);
matrix.getValues(values);
- assertEquals(6f, values[Matrix.MTRANS_X], 0f);
+ assertEquals(OFFSET_SUM, values[Matrix.MTRANS_X], 0f);
assertEquals(0f, values[Matrix.MTRANS_Y], 0f);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index dc461d1..d1cf1c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -20,8 +20,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Process.SYSTEM_UID;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -38,33 +36,27 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
import static org.mockito.Mockito.mock;
-
import android.content.Context;
import android.content.res.Configuration;
-import android.hardware.display.DisplayManagerGlobal;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.IWindow;
-import android.view.Surface;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
import com.android.server.AttributeCache;
-import com.android.server.wm.utils.MockTracker;
import org.junit.After;
-import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
-import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
@@ -79,10 +71,6 @@
WindowManagerService mWm;
private final IWindow mIWindow = new TestIWindow();
private Session mMockSession;
- // The default display is removed in {@link #setUp} and then we iterate over all displays to
- // make sure we don't collide with any existing display. If we run into no other display, the
- // added display should be treated as default. This cannot be the default display
- private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
static int sNextStackId = 1000;
/** Non-default display. */
@@ -99,8 +87,6 @@
WindowState mChildAppWindowBelow;
HashSet<WindowState> mCommonWindows;
- private MockTracker mMockTracker;
-
/**
* Spied {@link Transaction} class than can be used to verify calls.
*/
@@ -112,49 +98,27 @@
@Rule
public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
- static WindowState.PowerManagerWrapper sPowerManagerWrapper;
-
@BeforeClass
public static void setUpOnceBase() {
AttributeCache.init(getInstrumentation().getTargetContext());
-
- sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
- }
-
- @AfterClass
- public static void tearDownOnceBase() throws IOException {
- sPowerManagerWrapper = null;
}
@Before
public void setUpBase() {
- mMockTracker = new MockTracker();
-
// If @Before throws an exception, the error isn't logged. This will make sure any failures
// in the set up are clear. This can be removed when b/37850063 is fixed.
try {
mMockSession = mock(Session.class);
- mTransaction = spy(StubTransaction.class);
final Context context = getInstrumentation().getTargetContext();
mWm = mSystemServicesTestRule.getWindowManagerService();
-
- // Setup factory classes to prevent calls to native code.
-
- // Return a spied Transaction class than can be used to verify calls.
- mWm.mTransactionFactory = () -> mTransaction;
- // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances.
- mWm.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder();
- // Return mocked Surface instances.
- mWm.mSurfaceFactory = () -> mock(Surface.class);
+ mTransaction = mSystemServicesTestRule.mTransaction;
beforeCreateDisplay();
context.getDisplay().getDisplayInfo(mDisplayInfo);
mDisplayContent = createNewDisplay();
- mWm.mDisplayEnabled = true;
- mWm.mDisplayReady = true;
// Set-up some common windows.
mCommonWindows = new HashSet<>();
@@ -211,12 +175,6 @@
nonCommonWindows.pollLast().removeImmediately();
}
- for (int i = mWm.mRoot.mChildren.size() - 1; i >= 0; --i) {
- final DisplayContent displayContent = mWm.mRoot.mChildren.get(i);
- if (!displayContent.isDefaultDisplay) {
- displayContent.removeImmediately();
- }
- }
// Remove app transition & window freeze timeout callbacks to prevent unnecessary
// actions after test.
mWm.getDefaultDisplayContentLocked().mAppTransition
@@ -230,11 +188,7 @@
} catch (Exception e) {
Log.e(TAG, "Failed to tear down test", e);
throw e;
- } finally {
- mMockTracker.close();
- mMockTracker = null;
}
-
}
private WindowState createCommonWindow(WindowState parent, int type, String name) {
@@ -356,12 +310,13 @@
WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
int ownerId, boolean ownerCanAddInternalSystemWindow) {
return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
- mWm, mMockSession, mIWindow);
+ mWm, mMockSession, mIWindow, mSystemServicesTestRule.getPowerManagerWrapper());
}
static WindowState createWindow(WindowState parent, int type, WindowToken token,
String name, int ownerId, boolean ownerCanAddInternalSystemWindow,
- WindowManagerService service, Session session, IWindow iWindow) {
+ WindowManagerService service, Session session, IWindow iWindow,
+ WindowState.PowerManagerWrapper powerManagerWrapper) {
synchronized (service.mGlobalLock) {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
attrs.setTitle(name);
@@ -369,7 +324,7 @@
final WindowState w = new WindowState(service, session, iWindow, token, parent,
OP_NONE,
0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow,
- sPowerManagerWrapper);
+ powerManagerWrapper);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
// adding it to the token...
token.addWindow(w);
@@ -408,13 +363,11 @@
}
/** Creates a {@link DisplayContent} and adds it to the system. */
- DisplayContent createNewDisplay(DisplayInfo displayInfo) {
- final int displayId = sNextDisplayId++;
- final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
- displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- synchronized (mWm.mGlobalLock) {
- return new DisplayContent(display, mWm, mock(ActivityDisplay.class));
- }
+ DisplayContent createNewDisplay(DisplayInfo info) {
+ final ActivityDisplay display =
+ TestActivityDisplay.create(mWm.mAtmService.mStackSupervisor, info);
+ mWm.mAtmService.mRootActivityContainer.addChild(display, POSITION_TOP);
+ return display.mDisplayContent;
}
/**
@@ -428,17 +381,7 @@
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.copyFrom(mDisplayInfo);
displayInfo.state = displayState;
- final int displayId = sNextDisplayId++;
- final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
- displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- synchronized (mWm.mGlobalLock) {
- // Display creation is driven by DisplayWindowController via ActivityStackSupervisor.
- // We skip those steps here.
- final ActivityDisplay mockAd = mock(ActivityDisplay.class);
- final DisplayContent displayContent = mWm.mRoot.createDisplayContent(display, mockAd);
- displayContent.reconfigureDisplayLocked();
- return displayContent;
- }
+ return createNewDisplay(displayInfo);
}
/** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
index a6e675a..7f09482 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
@@ -89,7 +89,7 @@
for (final Object mock : mMocks.keySet()) {
if (MockUtil.isMock(mock)) {
- Mockito.reset(mock);
+ mMockitoFramework.clearInlineMock(mock);
}
}
mMocks.clear();
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 43fb304..42a5501 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -692,7 +692,7 @@
}
/**
- * Contains all sent text-based SMS messages in the SMS app.
+ * Contains all draft text-based SMS messages in the SMS app.
*/
public static final class Draft implements BaseColumns, TextBasedSmsColumns {
@@ -808,7 +808,15 @@
}
/**
- * Contains all sent text-based SMS messages in the SMS app.
+ * Contains a view of SMS conversations (also referred to as threads). This is similar to
+ * {@link Threads}, but only includes SMS messages and columns relevant to SMS
+ * conversations.
+ * <p>
+ * Note that this view ignores any information about MMS messages, it is a
+ * view of conversations as if MMS messages did not exist at all. This means that all
+ * relevant information, such as snippets and message count, will ignore any MMS messages
+ * that might be in the same thread through other views and present only data based on the
+ * SMS messages in that thread.
*/
public static final class Conversations
implements BaseColumns, TextBasedSmsColumns {