diff options
178 files changed, 5280 insertions, 3617 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 2fd2e33bbc37..9b290c6a4760 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -12,8 +12,6 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp services/incremental/ [Hook Scripts] -checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} - strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT} hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT} diff --git a/api/test-current.txt b/api/test-current.txt index f77922812ba1..ab0761374570 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5004,7 +5004,6 @@ package android.util { field public static final String PERSIST_PREFIX = "persist.sys.fflag.override."; field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer"; - field public static final String SETTINGS_FUSE_FLAG = "settings_fuse"; field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2"; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 8c54c242e04a..02c0d933b3cd 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -4590,7 +4590,7 @@ message GeneralExternalStorageAccessStats { // Includes file path and ContentResolver accesses optional uint32 secondary_storage_accesses = 4; // Comma-separated list of mime types that were accessed. - optional MimeTypes mime_types_accessed = 5; + optional MimeTypes mime_types_accessed = 5 [(log_mode) = MODE_BYTES]; } /** @@ -6174,7 +6174,7 @@ message ProcessStatsAvailablePagesProto { * Pulled from ProcessStatsService.java */ message ProcStats { - optional ProcessStatsSectionProto proc_stats_section = 1; + optional ProcessStatsSectionProto proc_stats_section = 1 [(log_mode) = MODE_BYTES]; // Data pulled from device into this is sometimes sharded across multiple atoms to work around // a size limit. When this happens, this shard ID will contain an increasing 1-indexed integer // with the number of this shard. @@ -6185,7 +6185,7 @@ message ProcStats { * Pulled from ProcessStatsService.java */ message ProcStatsPkgProc { - optional ProcessStatsSectionProto proc_stats_section = 1; + optional ProcessStatsSectionProto proc_stats_section = 1 [(log_mode) = MODE_BYTES]; } // Next Tag: 2 @@ -6203,7 +6203,7 @@ message NotificationRemoteViewsProto { * Pulled from NotificationManagerService.java */ message NotificationRemoteViews { - optional NotificationRemoteViewsProto notification_remote_views = 1; + optional NotificationRemoteViewsProto notification_remote_views = 1 [(log_mode) = MODE_BYTES]; } /** @@ -6271,7 +6271,7 @@ message DNDModeProto { // May also be "MANUAL_RULE" to indicate app-activation of the manual rule. optional string id = 5; optional int32 uid = 6 [(is_uid) = true]; // currently only SYSTEM_UID or 0 for other - optional DNDPolicyProto policy = 7; + optional DNDPolicyProto policy = 7 [(log_mode) = MODE_BYTES]; } /** @@ -6436,7 +6436,7 @@ message PowerProfileProto { * Pulled from PowerProfile.java */ message PowerProfile { - optional PowerProfileProto power_profile = 1; + optional PowerProfileProto power_profile = 1 [(log_mode) = MODE_BYTES]; } /** @@ -8131,7 +8131,7 @@ message DeviceIdentifierAccessDenied { message TrainInfo { optional int64 train_version_code = 1; - optional TrainExperimentIds train_experiment_id = 2; + optional TrainExperimentIds train_experiment_id = 2 [(log_mode) = MODE_BYTES]; optional string train_name = 3; @@ -11179,8 +11179,8 @@ message BlobInfo { optional int64 expiry_timestamp_millis = 3; // List of committers of this Blob - optional BlobCommitterListProto committers = 4; + optional BlobCommitterListProto committers = 4 [(log_mode) = MODE_BYTES]; // List of leasees of this Blob - optional BlobLeaseeListProto leasees = 5; + optional BlobLeaseeListProto leasees = 5 [(log_mode) = MODE_BYTES]; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index a2947a967d88..9b13d256aea6 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -271,9 +271,6 @@ public final class ActivityThread extends ClientTransactionHandler { /** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */ public static final int SERVICE_DONE_EXECUTING_STOP = 2; - // Whether to invoke an activity callback after delivering new configuration. - private static final boolean REPORT_TO_ACTIVITY = true; - /** Use foreground GC policy (less pause time) and higher JIT weight. */ private static final int VM_PROCESS_STATE_JANK_PERCEPTIBLE = 0; /** Use background GC policy and default JIT threshold. */ @@ -4973,7 +4970,8 @@ public final class ActivityThread extends ClientTransactionHandler { private void relaunchAllActivities(boolean preserveWindows) { for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) { final ActivityClientRecord r = entry.getValue(); - if (!r.activity.mFinished) { + // Schedule relaunch the activity if it is not a local object or finishing. + if (!r.activity.mFinished && !(r.token instanceof Binder)) { if (preserveWindows && r.window != null) { r.mPreserveWindow = true; } @@ -5539,18 +5537,14 @@ public final class ActivityThread extends ClientTransactionHandler { } /** - * Updates the configuration for an Activity. The ActivityClientRecord's - * {@link ActivityClientRecord#overrideConfig} is used to compute the final Configuration for - * that Activity. {@link ActivityClientRecord#tmpConfig} is used as a temporary for delivering - * the updated Configuration. - * @param r ActivityClientRecord representing the Activity. - * @param newBaseConfig The new configuration to use. This may be augmented with - * {@link ActivityClientRecord#overrideConfig}. + * Updates the configuration for an Activity in its current display. + * + * @see #performConfigurationChangedForActivity(ActivityClientRecord, Configuration, int, + * boolean) */ private void performConfigurationChangedForActivity(ActivityClientRecord r, Configuration newBaseConfig) { - performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId(), - false /* movedToDifferentDisplay */); + performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId()); } /** @@ -5562,17 +5556,16 @@ public final class ActivityThread extends ClientTransactionHandler { * @param newBaseConfig The new configuration to use. This may be augmented with * {@link ActivityClientRecord#overrideConfig}. * @param displayId The id of the display where the Activity currently resides. - * @param movedToDifferentDisplay Indicates if the activity was moved to different display. * @return {@link Configuration} instance sent to client, null if not sent. */ private Configuration performConfigurationChangedForActivity(ActivityClientRecord r, - Configuration newBaseConfig, int displayId, boolean movedToDifferentDisplay) { + Configuration newBaseConfig, int displayId) { r.tmpConfig.setTo(newBaseConfig); if (r.overrideConfig != null) { r.tmpConfig.updateFrom(r.overrideConfig); } final Configuration reportedConfig = performActivityConfigurationChanged(r.activity, - r.tmpConfig, r.overrideConfig, displayId, movedToDifferentDisplay); + r.tmpConfig, r.overrideConfig, displayId); freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig)); return reportedConfig; } @@ -5599,10 +5592,6 @@ public final class ActivityThread extends ClientTransactionHandler { * @param newConfig The new configuration. */ private void performConfigurationChanged(ComponentCallbacks2 cb, Configuration newConfig) { - if (!REPORT_TO_ACTIVITY) { - return; - } - // ContextThemeWrappers may override the configuration for that context. We must check and // apply any overrides defined. Configuration contextThemeWrapperOverrideConfig = null; @@ -5627,12 +5616,10 @@ public final class ActivityThread extends ClientTransactionHandler { * from the base global configuration. This is supplied by * ActivityManager. * @param displayId Id of the display where activity currently resides. - * @param movedToDifferentDisplay Indicates if the activity was moved to different display. * @return Configuration sent to client, null if no changes and not moved to different display. */ private Configuration performActivityConfigurationChanged(Activity activity, - Configuration newConfig, Configuration amOverrideConfig, int displayId, - boolean movedToDifferentDisplay) { + Configuration newConfig, Configuration amOverrideConfig, int displayId) { if (activity == null) { throw new IllegalArgumentException("No activity provided."); } @@ -5645,6 +5632,7 @@ public final class ActivityThread extends ClientTransactionHandler { // callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition handleWindowingModeChangeIfNeeded(activity, newConfig); + final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId); boolean shouldReportChange = false; if (activity.mCurrentConfig == null) { shouldReportChange = true; @@ -5658,8 +5646,7 @@ public final class ActivityThread extends ClientTransactionHandler { amOverrideConfig)) { // Nothing significant, don't proceed with updating and reporting. return null; - } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0 - || !REPORT_TO_ACTIVITY) { + } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) { // If this activity doesn't handle any of the config changes, then don't bother // calling onConfigurationChanged. Otherwise, report to the activity for the // changes. @@ -5693,11 +5680,6 @@ public final class ActivityThread extends ClientTransactionHandler { final Configuration configToReport = createNewConfigAndUpdateIfNotNull(newConfig, contextThemeWrapperOverrideConfig); - if (!REPORT_TO_ACTIVITY) { - // Not configured to report to activity. - return configToReport; - } - if (movedToDifferentDisplay) { activity.dispatchMovedToDisplay(displayId, configToReport); } @@ -5990,8 +5972,6 @@ public final class ActivityThread extends ClientTransactionHandler { if (DEBUG_CONFIGURATION) Slog.w(TAG, "Not found target activity to report to: " + r); return; } - final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY - && displayId != r.activity.getDisplayId(); synchronized (r) { if (overrideConfig.isOtherSeqNewer(r.mPendingOverrideConfig)) { @@ -6005,6 +5985,7 @@ public final class ActivityThread extends ClientTransactionHandler { r.mPendingOverrideConfig = null; } + final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId); if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig) && !movedToDifferentDisplay) { if (DEBUG_CONFIGURATION) { @@ -6020,29 +6001,34 @@ public final class ActivityThread extends ClientTransactionHandler { final ViewRootImpl viewRoot = r.activity.mDecor != null ? r.activity.mDecor.getViewRootImpl() : null; - if (movedToDifferentDisplay) { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity moved to display, activity:" - + r.activityInfo.name + ", displayId=" + displayId + if (DEBUG_CONFIGURATION) { + Slog.v(TAG, "Handle activity config changed, activity:" + + r.activityInfo.name + ", displayId=" + r.activity.getDisplayId() + + (movedToDifferentDisplay ? (", newDisplayId=" + displayId) : "") + ", config=" + overrideConfig); - - final Configuration reportedConfig = performConfigurationChangedForActivity(r, - mCompatConfiguration, displayId, true /* movedToDifferentDisplay */); - if (viewRoot != null) { - viewRoot.onMovedToDisplay(displayId, reportedConfig); - } - } else { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: " - + r.activityInfo.name + ", config=" + overrideConfig); - performConfigurationChangedForActivity(r, mCompatConfiguration); } + final Configuration reportedConfig = performConfigurationChangedForActivity(r, + mCompatConfiguration, + movedToDifferentDisplay ? displayId : r.activity.getDisplayId()); // Notify the ViewRootImpl instance about configuration changes. It may have initiated this // update to make sure that resources are updated before updating itself. if (viewRoot != null) { + if (movedToDifferentDisplay) { + viewRoot.onMovedToDisplay(displayId, reportedConfig); + } viewRoot.updateConfiguration(displayId); } mSomeActivitiesChanged = true; } + /** + * Checks if the display id of activity is different from the given one. Note that + * {@link #INVALID_DISPLAY} means no difference. + */ + private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) { + return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId(); + } + final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) { if (start) { try { diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index cfe0aff05d4a..e7b3e14bfda7 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -502,7 +502,7 @@ public final class ApplicationExitInfo implements Parcelable { * Return the defining kernel user identifier, maybe different from {@link #getRealUid} and * {@link #getPackageUid}, if an external service has the * {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set - * to <code>true<code> and was bound with the flag + * to <code>true</code> and was bound with the flag * {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here will * be the kernel user identifier of the external service provider. */ diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 79da1f6ab282..ee9bd3d259fb 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -1168,7 +1168,12 @@ public class IntentFilter implements Parcelable { public int match(Uri data, boolean wildcardSupported) { String host = data.getHost(); if (host == null) { - return NO_MATCH_DATA; + if (wildcardSupported && mWild) { + // special case, if no host is provided, but the Authority is wildcard, match + return MATCH_CATEGORY_HOST; + } else { + return NO_MATCH_DATA; + } } if (false) Log.v("IntentFilter", "Match host " + host + ": " + mHost); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 431c5d7310f1..3688f1bda979 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -1510,7 +1510,7 @@ public class ParsingPackageUtils { Uri data = null; String dataType = null; - String host = IntentFilter.WILDCARD; + String host = null; final int numActions = intentInfo.countActions(); final int numSchemes = intentInfo.countDataSchemes(); final int numTypes = intentInfo.countDataTypes(); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 5647bf90d2fb..c5a11abe1136 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -605,9 +605,6 @@ public class InputMethodService extends AbstractInputMethodService { if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding + " ic=" + mInputConnection); // Unbind input is per process per display. - // TODO(b/150902448): free-up IME surface when target is changing. - // e.g. DisplayContent#setInputMethodTarget() - removeImeSurface(); onUnbindInput(); mInputBinding = null; mInputConnection = null; diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 0c81e5a23291..a0207c8497c8 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -127,6 +127,7 @@ public class Binder implements IBinder { * * @hide */ + @CriticalNative public static native int getNativeTid(); // Use a Holder to allow static initialization of Binder in the boot image, and diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index 7c42c36e7747..64ab1d711765 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -96,6 +96,15 @@ public abstract class HwBinder implements IHwBinder { throws RemoteException, NoSuchElementException; /** + * This allows getService to bypass the VINTF manifest for testing only. + * + * Disabled on user builds. + * @hide + */ + public static native final void setTrebleTestingOverride( + boolean testingOverride); + + /** * Configures how many threads the process-wide hwbinder threadpool * has to process incoming requests. * diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 0abf8ae352af..6b5eb16d7bff 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -86,7 +86,6 @@ import android.system.Os; import android.system.OsConstants; import android.text.TextUtils; import android.util.DataUnit; -import android.util.FeatureFlagUtils; import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -161,11 +160,6 @@ public class StorageManager { /** {@hide} */ public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot"; /** {@hide} */ - public static final String PROP_FUSE = "persist.sys.fuse"; - /** {@hide} */ - public static final String PROP_SETTINGS_FUSE = FeatureFlagUtils.PERSIST_PREFIX - + FeatureFlagUtils.SETTINGS_FUSE_FLAG; - /** {@hide} */ public static final String PROP_FORCED_SCOPED_STORAGE_WHITELIST = "forced_scoped_storage_whitelist"; diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 537498c44d5e..e338fd977f27 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -41,7 +41,6 @@ public class FeatureFlagUtils { public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; public static final String DYNAMIC_SYSTEM = "settings_dynamic_system"; public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2"; - public static final String SETTINGS_FUSE_FLAG = "settings_fuse"; /** @hide */ public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED = "settings_do_not_restore_preserved"; @@ -52,7 +51,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS = new HashMap<>(); DEFAULT_FLAGS.put("settings_audio_switcher", "true"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); - DEFAULT_FLAGS.put(SETTINGS_FUSE_FLAG, "true"); DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false"); DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false"); diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index ef9d990168d2..c1998c6009cf 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -119,11 +119,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching // this code here means that we now got control, so we can start the animation immediately. // If client window is trying to control IME and IME is already visible, it is immediate. - if (fromIme || mState.getSource(getType()).isVisible()) { + if (fromIme || mState.getSource(getType()).isVisible() && getControl() != null) { return ShowResult.SHOW_IMMEDIATELY; } - return getImm().requestImeShow(null /* resultReceiver */) + return getImm().requestImeShow(mController.getHost().getWindowToken()) ? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED; } @@ -132,12 +132,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { */ @Override void notifyHidden() { - getImm().notifyImeHidden(); + getImm().notifyImeHidden(mController.getHost().getWindowToken()); } @Override public void removeSurface() { - getImm().removeImeSurface(); + final IBinder window = mController.getHost().getWindowToken(); + if (window != null) { + getImm().removeImeSurface(window); + } } @Override @@ -146,6 +149,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { super.setControl(control, showTypes, hideTypes); if (control == null && !mIsRequestedVisibleAwaitingControl) { hide(); + removeSurface(); } } diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 40e6f4b2fce8..700dc66fab55 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -153,6 +153,9 @@ public class InsetsSourceConsumer { if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) { applyHiddenToControl(); } + if (!requestedVisible && !mIsAnimationPending) { + removeSurface(); + } } } if (lastControl != null) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 81a78dc177ec..b022d2af7772 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2717,7 +2717,6 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer.isEnabled()) { mAttachInfo.mThreadedRenderer.destroy(); } - notifySurfaceDestroyed(); } else if ((surfaceReplaced || surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged) && mSurfaceHolder == null @@ -2948,6 +2947,10 @@ public final class ViewRootImpl implements ViewParent, } } + if (surfaceDestroyed) { + notifySurfaceDestroyed(); + } + if (triggerGlobalLayoutListener) { mAttachInfo.mRecomputeGlobalAttributes = false; mAttachInfo.mTreeObserver.dispatchOnGlobalLayout(); @@ -9382,6 +9385,11 @@ public final class ViewRootImpl implements ViewParent, return mInputEventReceiver.getToken(); } + @NonNull + public IBinder getWindowToken() { + return mAttachInfo.mWindowToken; + } + /** * Class for managing the accessibility interaction connection * based on the global accessibility state. diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 864c40f8a740..fa92e29cbf58 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2111,28 +2111,36 @@ public final class InputMethodManager { /** * Call showSoftInput with currently focused view. - * @return {@code true} if IME can be shown. + * + * @param windowToken the window from which this request originates. If this doesn't match the + * currently served view, the request is ignored and returns {@code false}. + * + * @return {@code true} if IME can (eventually) be shown, {@code false} otherwise. * @hide */ - public boolean requestImeShow(ResultReceiver resultReceiver) { + public boolean requestImeShow(IBinder windowToken) { synchronized (mH) { final View servedView = getServedViewLocked(); - if (servedView == null) { + if (servedView == null || servedView.getWindowToken() != windowToken) { return false; } - showSoftInput(servedView, 0 /* flags */, resultReceiver); + showSoftInput(servedView, 0 /* flags */, null /* resultReceiver */); return true; } } /** * Notify IME directly that it is no longer visible. + * + * @param windowToken the window from which this request originates. If this doesn't match the + * currently served view, the request is ignored. * @hide */ - public void notifyImeHidden() { + public void notifyImeHidden(IBinder windowToken) { synchronized (mH) { try { - if (mCurMethod != null) { + if (mCurMethod != null && mCurRootView != null + && mCurRootView.getWindowToken() == windowToken) { mCurMethod.notifyImeHidden(); } } catch (RemoteException re) { @@ -2142,15 +2150,15 @@ public final class InputMethodManager { /** * Notify IME directly to remove surface as it is no longer visible. + * @param windowToken The client window token that requests the IME to remove its surface. * @hide */ - public void removeImeSurface() { + public void removeImeSurface(IBinder windowToken) { synchronized (mH) { try { - if (mCurMethod != null) { - mCurMethod.removeImeSurface(); - } - } catch (RemoteException re) { + mService.removeImeSurfaceFromWindow(windowToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 0b46658d5a06..0a3fe096279d 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -21,8 +21,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COM import static android.view.View.SYSTEM_UI_LAYOUT_FLAGS; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static android.view.WindowInsets.Type.ime; -import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; @@ -34,8 +32,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import android.annotation.NonNull; import android.annotation.Nullable; @@ -146,17 +142,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if ((view.getWindowSystemUiVisibility() & SYSTEM_UI_LAYOUT_FLAGS) != 0) { return new Pair<>(Insets.NONE, insets); } - - boolean includeIme = (view.getViewRootImpl().mWindowAttributes.softInputMode - & SOFT_INPUT_MASK_ADJUST) - == SOFT_INPUT_ADJUST_RESIZE; - Insets insetsToApply; - if (ViewRootImpl.sNewInsetsMode == 0) { - insetsToApply = insets.getSystemWindowInsets(); - } else { - insetsToApply = insets.getInsets(systemBars() | (includeIme ? ime() : 0)); - } - insets = insets.inset(insetsToApply); + Insets insetsToApply = insets.getSystemWindowInsets(); return new Pair<>(insetsToApply, insets.inset(insetsToApply).consumeSystemWindowInsets()); }; diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 8ec51b89d240..a1cbd3fcae79 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -73,5 +73,8 @@ interface IInputMethodManager { in float[] matrixValues); oneway void reportPerceptible(in IBinder windowToken, boolean perceptible); + /** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */ void removeImeSurface(); + /** Remove the IME surface. Requires passing the currently focused window. */ + void removeImeSurfaceFromWindow(in IBinder windowToken); } diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index b6427c9aa01c..48f33a6a3d77 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -339,6 +339,10 @@ static jobject JHwBinder_native_getService( return JHwRemoteBinder::NewObject(env, service); } +void JHwBinder_native_setTrebleTestingOverride(JNIEnv*, jclass, jboolean testingOverride) { + hardware::details::setTrebleTestingOverride(testingOverride); +} + void JHwBinder_native_configureRpcThreadpool(JNIEnv *, jclass, jlong maxThreads, jboolean callerWillJoin) { CHECK(maxThreads > 0); @@ -368,6 +372,9 @@ static JNINativeMethod gMethods[] = { { "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;", (void *)JHwBinder_native_getService }, + { "setTrebleTestingOverride", "(Z)V", + (void *)JHwBinder_native_setTrebleTestingOverride }, + { "configureRpcThreadpool", "(JZ)V", (void *)JHwBinder_native_configureRpcThreadpool }, diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index aef53a4b3318..885b0a33e43c 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -1048,7 +1048,7 @@ static void android_os_Binder_setExtension(JNIEnv* env, jobject obj, jobject ext jbh->setExtension(extension); } -JNIEXPORT jint JNICALL android_os_Binder_getNativeTid(JNIEnv* env, jobject clazz) { +static jint android_os_Binder_getNativeTid() { return (jint)android::base::GetThreadId(); } @@ -1083,6 +1083,7 @@ static const JNINativeMethod gBinderMethods[] = { { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable }, { "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension }, { "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension }, + // @CriticalNative { "getNativeTid", "()I", (void*)android_os_Binder_getNativeTid }, }; diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 6e49c0dda1ad..b7c5289043d6 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -124,7 +124,6 @@ typedef const std::function<void(std::string)>& fail_fn_t; static pid_t gSystemServerPid = 0; static constexpr const char* kVoldAppDataIsolation = "persist.sys.vold_app_data_isolation_enabled"; -static constexpr const char* kPropFuse = "persist.sys.fuse"; static const char kZygoteClassName[] = "com/android/internal/os/Zygote"; static jclass gZygoteClass; static jmethodID gCallPostForkSystemServerHooks; @@ -836,29 +835,20 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL, multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn); - bool isFuse = GetBoolProperty(kPropFuse, false); bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false); - if (isFuse) { - if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) { + if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) { const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id); PrepareDir(pass_through_source, 0710, AID_ROOT, AID_MEDIA_RW, fail_fn); BindMount(pass_through_source, "/storage", fail_fn); - } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) { + } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) { const std::string installer_source = StringPrintf("/mnt/installer/%d", user_id); BindMount(installer_source, "/storage", fail_fn); - } else if (isAppDataIsolationEnabled && mount_mode == MOUNT_EXTERNAL_ANDROID_WRITABLE) { + } else if (isAppDataIsolationEnabled && mount_mode == MOUNT_EXTERNAL_ANDROID_WRITABLE) { const std::string writable_source = StringPrintf("/mnt/androidwritable/%d", user_id); BindMount(writable_source, "/storage", fail_fn); - } else { - BindMount(user_source, "/storage", fail_fn); - } } else { - const std::string& storage_source = ExternalStorageViews[mount_mode]; - BindMount(storage_source, "/storage", fail_fn); - - // Mount user-specific symlink helper into place - BindMount(user_source, "/storage/self", fail_fn); + BindMount(user_source, "/storage", fail_fn); } } diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index ce08e43cb1af..d312d859a58c 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1152,7 +1152,7 @@ <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Capturar imagen con %1$s"</string> <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Capturar imagen"</string> <string name="alwaysUse" msgid="3153558199076112903">"Usar siempre para esta acción"</string> - <string name="use_a_different_app" msgid="4987790276170972776">"Utiliza otra aplicación"</string> + <string name="use_a_different_app" msgid="4987790276170972776">"Usar otra aplicación"</string> <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Para borrar los valores predeterminados, accede a Ajustes del sistema > Aplicaciones > Descargadas."</string> <string name="chooseActivity" msgid="8563390197659779956">"Selecciona una acción"</string> <string name="chooseUsbActivity" msgid="2096269989990986612">"Elegir una aplicación para el dispositivo USB"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index eb675ddb06a5..485f5bf08224 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -1926,7 +1926,7 @@ <string name="time_picker_header_text" msgid="9073802285051516688">"Ezarri ordua"</string> <string name="time_picker_input_error" msgid="8386271930742451034">"Idatzi balio duen ordu bat"</string> <string name="time_picker_prompt_label" msgid="303588544656363889">"Idatzi ordua"</string> - <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Aldatu testu modura ordua zehazteko."</string> + <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Ordua idazteko, aldatu testua idazteko metodora."</string> <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Aldatu erloju modura ordua zehazteko."</string> <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Betetze automatikoaren aukerak"</string> <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Gorde betetze automatikoarekin erabiltzeko"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 8d9df1f2b09b..bd328b542966 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -857,7 +857,7 @@ <string name="emergency_calls_only" msgid="3057351206678279851">"Só chamadas de emerxencia"</string> <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Bloqueada pola rede"</string> <string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"A tarxeta SIM está bloqueada con código PUK."</string> - <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta a guía do usuario ou ponte en contacto co servizo de asistencia ao cliente."</string> + <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta a guía para usuarios ou ponte en contacto co servizo de asistencia ao cliente."</string> <string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"A tarxeta SIM está bloqueada."</string> <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"Desbloqueando tarxeta SIM…"</string> <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index e74157f2e02b..eabf37c50878 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1152,7 +1152,7 @@ <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"「%1$s」を使用して画像をキャプチャ"</string> <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"画像をキャプチャ"</string> <string name="alwaysUse" msgid="3153558199076112903">"常にこの操作で使用する"</string> - <string name="use_a_different_app" msgid="4987790276170972776">"別のアプリの使用"</string> + <string name="use_a_different_app" msgid="4987790276170972776">"別のアプリを使用"</string> <string name="clearDefaultHintMsg" msgid="1325866337702524936">"[システム設定]>[アプリ]>[ダウンロード済み]でデフォルト設定をクリアします。"</string> <string name="chooseActivity" msgid="8563390197659779956">"操作の選択"</string> <string name="chooseUsbActivity" msgid="2096269989990986612">"USBデバイス用アプリを選択"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index affbc838048b..351790d44b7b 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1547,7 +1547,7 @@ <string name="sending" msgid="206925243621664438">"Жіберілуде..."</string> <string name="launchBrowserDefault" msgid="6328349989932924119">"Браузер қосылсын ба?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"Қоңырауды қабылдау?"</string> - <string name="activity_resolver_use_always" msgid="5575222334666843269">"Үнемі"</string> + <string name="activity_resolver_use_always" msgid="5575222334666843269">"Әрқашан"</string> <string name="activity_resolver_use_once" msgid="948462794469672658">"Бір рет қана"</string> <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s жұмыс профилін қолдамайды"</string> <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Планшет"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index e250b50e9821..f1bea56a62fd 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -538,7 +538,7 @@ <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Колдонмого сүрөт жыйнагыңызды өзгөртүүгө мүмкүнчүлүк берет."</string> <string name="permlab_mediaLocation" msgid="7368098373378598066">"медиа жыйнагыңыз сакталган жерлерди окуу"</string> <string name="permdesc_mediaLocation" msgid="597912899423578138">"Колдонмого медиа жыйнагыңыз сакталган жерлерди окууга мүмкүнчүлүк берет."</string> - <string name="biometric_dialog_default_title" msgid="55026799173208210">"Сиз экениңизди ырастаңыз"</string> + <string name="biometric_dialog_default_title" msgid="55026799173208210">"Өзүңүздү ырастаңыз"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалык аппарат жеткиликсиз"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аныктыгын текшерүү жокко чыгарылды"</string> <string name="biometric_not_recognized" msgid="5106687642694635888">"Таанылган жок"</string> @@ -1547,7 +1547,7 @@ <string name="sending" msgid="206925243621664438">"Жөнөтүлүүдө…"</string> <string name="launchBrowserDefault" msgid="6328349989932924119">"Серепчи иштетилсинби?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"Чалуу кабыл алынсынбы?"</string> - <string name="activity_resolver_use_always" msgid="5575222334666843269">"Дайыма"</string> + <string name="activity_resolver_use_always" msgid="5575222334666843269">"Ар дайым"</string> <string name="activity_resolver_use_once" msgid="948462794469672658">"Бир жолу гана"</string> <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s жумуш профилин колдоого албайт"</string> <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Планшет"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 51737ba80550..6ccfc09a46a0 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1792,8 +1792,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"已由你的管理員更新"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由你的管理員刪除"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string> - <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n"<annotation id="url">"瞭解詳情"</annotation></string> - <string name="battery_saver_description" msgid="8587408568232177204">"為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞"</string> + <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"為了延長電池續航力,省電模式會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n"<annotation id="url">"瞭解詳情"</annotation></string> + <string name="battery_saver_description" msgid="8587408568232177204">"為了延長電池續航力,省電模式會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞"</string> <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你目前使用的應用程式可以存取資料,但存取頻率可能不如平時高。舉例來說,圖片可能不會自動顯示,在你輕觸後才會顯示。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟數據節省模式嗎?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string> @@ -2000,9 +2000,9 @@ <string name="notification_feedback_indicator" msgid="663476517711323016">"提供意見"</string> <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"日常安排模式資訊通知"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"電池電力可能會在你平常的充電時間前耗盡"</string> - <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用節約耗電量模式以延長電池續航力"</string> - <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"節約耗電量"</string> - <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"節約耗電量模式已關閉"</string> + <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用省電模式以延長電池續航力"</string> + <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"省電模式"</string> + <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"省電模式已關閉"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"手機電力充足,各項功能不再受到限制。"</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"平板電腦電力充足,各項功能不再受到限制。"</string> <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"裝置電力充足,各項功能不再受到限制。"</string> diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 1b3272572db0..7efd616c5607 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -71,6 +71,9 @@ public class InsetsSourceConsumerTest { private SurfaceControl mLeash; @Mock Transaction mMockTransaction; private InsetsSource mSpyInsetsSource; + private boolean mRemoveSurfaceCalled = false; + private InsetsController mController; + private InsetsState mState; @Before public void setup() { @@ -89,13 +92,19 @@ public class InsetsSourceConsumerTest { } catch (BadTokenException e) { // activity isn't running, lets ignore BadTokenException. } - InsetsState state = new InsetsState(); + mState = new InsetsState(); mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR)); - state.addSource(mSpyInsetsSource); - - mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, state, - () -> mMockTransaction, - new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl))); + mState.addSource(mSpyInsetsSource); + + mController = new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl)); + mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState, + () -> mMockTransaction, mController) { + @Override + public void removeSurface() { + super.removeSurface(); + mRemoveSurfaceCalled = true; + } + }; }); instrumentation.waitForIdleSync(); @@ -171,6 +180,25 @@ public class InsetsSourceConsumerTest { mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()), new int[1], hideTypes); assertEquals(statusBars(), hideTypes[0]); + assertFalse(mRemoveSurfaceCalled); + }); + } + + @Test + public void testRestore_noAnimation() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + mConsumer.hide(); + mController.onStateChanged(mState); + mConsumer.setControl(null, new int[1], new int[1]); + reset(mMockTransaction); + verifyZeroInteractions(mMockTransaction); + mRemoveSurfaceCalled = false; + int[] hideTypes = new int[1]; + mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()), + new int[1], hideTypes); + assertTrue(mRemoveSurfaceCalled); + assertEquals(0, hideTypes[0]); }); + } } diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index aa9dd40f5e0f..c0b8e1bf3bbe 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -260,7 +260,9 @@ public class LocationManager { * {@code OP_MONITOR_HIGH_POWER_LOCATION}. * * @hide + * @deprecated This action is unnecessary from Android S forward. */ + @Deprecated public static final String HIGH_POWER_REQUEST_CHANGE_ACTION = "android.location.HIGH_POWER_REQUEST_CHANGE"; diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 515d610109ab..df022d562768 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -447,7 +447,7 @@ jobjectArray FilterCallback::getMediaEvent( if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) { sp<MediaEvent> mediaEventSp = new MediaEvent(mIFilter, mediaEvent.avMemory, - mediaEvent.avDataId, dataLength, obj); + mediaEvent.avDataId, dataLength + offset, obj); mediaEventSp->mAvHandleRefCnt++; env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get()); mediaEventSp->incStrong(obj); diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp index 32b33a758535..8598f74e1441 100644 --- a/packages/CarSystemUI/Android.bp +++ b/packages/CarSystemUI/Android.bp @@ -49,7 +49,7 @@ android_library { "androidx.lifecycle_lifecycle-extensions", "SystemUI-tags", "SystemUI-proto", - "dagger2-2.19", + "dagger2", "//external/kotlinc:kotlin-annotations", ], @@ -59,7 +59,7 @@ android_library { manifest: "AndroidManifest.xml", - plugins: ["dagger2-compiler-2.19"], + plugins: ["dagger2-compiler"], } @@ -104,7 +104,7 @@ android_library { "mockito-target-inline-minus-junit4", "testables", "truth-prebuilt", - "dagger2-2.19", + "dagger2", "//external/kotlinc:kotlin-annotations", ], libs: [ @@ -118,7 +118,7 @@ android_library { "com.android.systemui", ], - plugins: ["dagger2-compiler-2.19"], + plugins: ["dagger2-compiler"], } android_app { @@ -157,7 +157,7 @@ android_app { kotlincflags: ["-Xjvm-default=enable"], - plugins: ["dagger2-compiler-2.19"], + plugins: ["dagger2-compiler"], required: ["privapp_whitelist_com.android.systemui"], } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java index eca51e34995c..232df2eced39 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java @@ -24,6 +24,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadsetClient; import android.content.Intent; import android.os.Handler; @@ -44,14 +46,19 @@ import org.junit.runner.RunWith; public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + private static final String BLUETOOTH_REMOTE_ADDRESS = "00:11:22:33:44:55"; private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier; + private TestableLooper mTestableLooper; private Handler mTestHandler; + private BluetoothDevice mBluetoothDevice; @Before public void setUp() throws Exception { - TestableLooper testableLooper = TestableLooper.get(this); - mTestHandler = spy(new Handler(testableLooper.getLooper())); + mTestableLooper = TestableLooper.get(this); + mTestHandler = spy(new Handler(mTestableLooper.getLooper())); + mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( + BLUETOOTH_REMOTE_ADDRESS); mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier( mContext, mTestHandler); mVoiceRecognitionNotifier.onBootCompleted(); @@ -61,8 +68,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { public void testReceiveIntent_started_showToast() { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, VOICE_RECOGNITION_STARTED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); - waitForIdleSync(); + mTestableLooper.processAllMessages(); verify(mTestHandler).post(any()); } @@ -71,8 +80,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { public void testReceiveIntent_invalidExtra_noToast() { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, INVALID_VALUE); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); - waitForIdleSync(); + mTestableLooper.processAllMessages(); verify(mTestHandler, never()).post(any()); } @@ -80,8 +91,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { @Test public void testReceiveIntent_noExtra_noToast() { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); - waitForIdleSync(); + mTestableLooper.processAllMessages(); verify(mTestHandler, never()).post(any()); } @@ -89,8 +102,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { @Test public void testReceiveIntent_invalidIntent_noToast() { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); - waitForIdleSync(); + mTestableLooper.processAllMessages(); verify(mTestHandler, never()).post(any()); } diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java index 9ae31989eeb2..5a756fe50209 100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java @@ -46,7 +46,7 @@ public class RecommendationServiceImpl extends RecommendationService private static final String LOG_TAG = "PrintServiceRecService"; /** All registered plugins */ - private ArrayList<RemotePrintServicePlugin> mPlugins; + private final ArrayList<RemotePrintServicePlugin> mPlugins = new ArrayList<>(); /** Lock to keep multi-cast enabled */ private WifiManager.MulticastLock mMultiCastLock; @@ -62,8 +62,6 @@ public class RecommendationServiceImpl extends RecommendationService mMultiCastLock.acquire(); } - mPlugins = new ArrayList<>(); - try { for (VendorConfig config : VendorConfig.getAllConfigs(this)) { try { @@ -138,6 +136,7 @@ public class RecommendationServiceImpl extends RecommendationService Log.e(LOG_TAG, "Could not stop plugin", e); } } + mPlugins.clear(); if (mMultiCastLock != null) { mMultiCastLock.release(); diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 7a27676237a1..6ecf303c6dc8 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -68,14 +68,14 @@ android_library { "iconloader_base", "SystemUI-tags", "SystemUI-proto", - "dagger2-2.19", + "dagger2", "jsr330" ], manifest: "AndroidManifest.xml", kotlincflags: ["-Xjvm-default=enable"], - plugins: ["dagger2-compiler-2.19"], + plugins: ["dagger2-compiler"], } filegroup { @@ -139,7 +139,7 @@ android_library { "mockito-target-extended-minus-junit4", "testables", "truth-prebuilt", - "dagger2-2.19", + "dagger2", "jsr330" ], libs: [ @@ -151,7 +151,7 @@ android_library { "--extra-packages", "com.android.systemui", ], - plugins: ["dagger2-compiler-2.19"], + plugins: ["dagger2-compiler"], } android_app { diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md index c440fba10135..bb68647ceb00 100644 --- a/packages/SystemUI/docs/dagger.md +++ b/packages/SystemUI/docs/dagger.md @@ -206,11 +206,31 @@ public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet a ## Updating Dagger2 +We depend on the Dagger source found in external/dagger2. We should automatically pick up on updates +when that repository is updated. + +*Deprecated:* + Binaries can be downloaded from https://repo1.maven.org/maven2/com/google/dagger/ and then loaded into [/prebuilts/tools/common/m2/repository/com/google/dagger/](http://cs/android/prebuilts/tools/common/m2/repository/com/google/dagger/) +The following commands should work, substituting in the version that you are looking for: + +```` +cd prebuilts/tools/common/m2/repository/com/google/dagger/ + +wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger/2.28.1/ + +wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-compiler/2.28.1/ + +wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-spi/2.28.1/ + +wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-producers/2.28.1/ +```` +Then update `prebuilts/tools/common/m2/Android.bp` to point at your new jars. + ## TODO List - Eliminate usages of Dependency#get diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index f474a36b99b7..732758a2ded2 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<com.google.android.systemui.udfps.UdfpsView +<com.android.systemui.biometrics.UdfpsView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/udfps_view" diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 941de2dc63ec..5fd7b53435cf 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -21,9 +21,9 @@ import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -57,12 +57,11 @@ public class AppOpsControllerImpl implements AppOpsController, private static final long NOTED_OP_TIME_DELAY_MS = 5000; private static final String TAG = "AppOpsControllerImpl"; private static final boolean DEBUG = false; - private final Context mContext; private final AppOpsManager mAppOps; private H mBGHandler; private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>(); - private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>(); + private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>(); private boolean mListening; @GuardedBy("mActiveItems") @@ -71,6 +70,7 @@ public class AppOpsControllerImpl implements AppOpsController, private final List<AppOpItem> mNotedItems = new ArrayList<>(); protected static final int[] OPS = new int[] { + AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, AppOpsManager.OP_CAMERA, AppOpsManager.OP_SYSTEM_ALERT_WINDOW, AppOpsManager.OP_RECORD_AUDIO, @@ -83,7 +83,6 @@ public class AppOpsControllerImpl implements AppOpsController, Context context, @Background Looper bgLooper, DumpManager dumpManager) { - mContext = context; mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mBGHandler = new H(bgLooper); final int numOps = OPS.length; @@ -131,7 +130,7 @@ public class AppOpsControllerImpl implements AppOpsController, boolean added = false; final int numCodes = opsCodes.length; for (int i = 0; i < numCodes; i++) { - if (mCallbacksByCode.containsKey(opsCodes[i])) { + if (mCallbacksByCode.contains(opsCodes[i])) { mCallbacksByCode.get(opsCodes[i]).add(callback); added = true; } else { @@ -155,7 +154,7 @@ public class AppOpsControllerImpl implements AppOpsController, public void removeCallback(int[] opsCodes, AppOpsController.Callback callback) { final int numCodes = opsCodes.length; for (int i = 0; i < numCodes; i++) { - if (mCallbacksByCode.containsKey(opsCodes[i])) { + if (mCallbacksByCode.contains(opsCodes[i])) { mCallbacksByCode.get(opsCodes[i]).remove(callback); } } @@ -312,7 +311,7 @@ public class AppOpsControllerImpl implements AppOpsController, } private void notifySuscribers(int code, int uid, String packageName, boolean active) { - if (mCallbacksByCode.containsKey(code)) { + if (mCallbacksByCode.contains(code)) { if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName); for (Callback cb: mCallbacksByCode.get(code)) { cb.onActiveStateChanged(code, uid, packageName, active); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index c82e1debf185..97e97ffaae0c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -22,6 +22,8 @@ import android.graphics.PixelFormat; import android.graphics.Point; import android.hardware.fingerprint.IFingerprintService; import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.Handler; +import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.view.LayoutInflater; @@ -44,6 +46,7 @@ class UdfpsController { private final Context mContext; private final IFingerprintService mFingerprintService; private final WindowManager mWindowManager; + private final Handler mHandler; private UdfpsView mView; private WindowManager.LayoutParams mLayoutParams; @@ -96,6 +99,7 @@ class UdfpsController { mContext = context; mFingerprintService = fingerprintService; mWindowManager = windowManager; + mHandler = new Handler(Looper.getMainLooper()); start(); } @@ -138,23 +142,27 @@ class UdfpsController { } private void showUdfpsOverlay() { - Log.v(TAG, "showUdfpsOverlay | adding window"); - if (!mIsOverlayShowing) { - try { - mWindowManager.addView(mView, mLayoutParams); - mIsOverlayShowing = true; - } catch (RuntimeException e) { - Log.e(TAG, "showUdfpsOverlay | failed to add window", e); + mHandler.post(() -> { + Log.v(TAG, "showUdfpsOverlay | adding window"); + if (!mIsOverlayShowing) { + try { + mWindowManager.addView(mView, mLayoutParams); + mIsOverlayShowing = true; + } catch (RuntimeException e) { + Log.e(TAG, "showUdfpsOverlay | failed to add window", e); + } } - } + }); } private void hideUdfpsOverlay() { - Log.v(TAG, "hideUdfpsOverlay | removing window"); - if (mIsOverlayShowing) { - mWindowManager.removeView(mView); - mIsOverlayShowing = false; - } + mHandler.post(() -> { + Log.v(TAG, "hideUdfpsOverlay | removing window"); + if (mIsOverlayShowing) { + mWindowManager.removeView(mView); + mIsOverlayShowing = false; + } + }); } private void onFingerDown(int x, int y, float minor, float major) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java index 40a93e1cdc47..57e2362bd511 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java @@ -24,7 +24,6 @@ import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; @@ -95,9 +94,16 @@ public class BubbleIconFactory extends BaseIconFactory { Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(), userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig()); Canvas c = new Canvas(badgeAndRing); - Rect dest = new Rect((int) ringStrokeWidth, (int) ringStrokeWidth, - c.getHeight() - (int) ringStrokeWidth, c.getWidth() - (int) ringStrokeWidth); - c.drawBitmap(userBadgedBitmap, null, dest, null); + + final int bitmapTop = (int) ringStrokeWidth; + final int bitmapLeft = (int) ringStrokeWidth; + final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth; + final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth; + + Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth, + bitmapHeight, /* filter */ true); + c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null); + Paint ringPaint = new Paint(); ringPaint.setStyle(Paint.Style.STROKE); ringPaint.setColor(importantConversationColor); @@ -105,6 +111,7 @@ public class BubbleIconFactory extends BaseIconFactory { ringPaint.setStrokeWidth(ringStrokeWidth); c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2 - ringStrokeWidth, ringPaint); + shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c); return createIconBitmap(badgeAndRing); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 0873328c2382..4bd046e23dab 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -24,6 +24,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.assist.AssistModule; +import com.android.systemui.doze.dagger.DozeComponent; import com.android.systemui.dump.DumpManager; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.model.SysUiState; @@ -68,6 +69,7 @@ import dagger.Provides; }, subcomponents = {StatusBarComponent.class, NotificationRowComponent.class, + DozeComponent.class, ExpandableNotificationRowComponent.class}) public abstract class SystemUIModule { diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java index 4fea45c39d5d..60ee806d0a9f 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java @@ -51,6 +51,14 @@ public class AlwaysOnDisplayPolicy { static final String KEY_WALLPAPER_VISIBILITY_MS = "wallpaper_visibility_timeout"; static final String KEY_WALLPAPER_FADE_OUT_MS = "wallpaper_fade_out_duration"; + + /** + * Integer used to dim the screen while dozing. + * + * @see R.integer.config_screenBrightnessDoze + */ + public int defaultDozeBrightness; + /** * Integer array to map ambient brightness type to real screen brightness. * @@ -165,6 +173,8 @@ public class AlwaysOnDisplayPolicy { DEFAULT_WALLPAPER_FADE_OUT_MS); wallpaperVisibilityDuration = mParser.getLong(KEY_WALLPAPER_VISIBILITY_MS, DEFAULT_WALLPAPER_VISIBILITY_MS); + defaultDozeBrightness = resources.getInteger( + com.android.internal.R.integer.config_screenBrightnessDoze); screenBrightnessArray = mParser.getIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY, resources.getIntArray( R.array.config_doze_brightness_sensor_to_brightness)); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java index abd41d4318bd..5eb9808ebd7c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java @@ -16,20 +16,22 @@ package com.android.systemui.doze; -import android.content.Context; - import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Dependency; +import com.android.systemui.doze.dagger.DozeScope; + +import javax.inject.Inject; /** * Controls removing Keyguard authorization when the phone goes to sleep. */ +@DozeScope public class DozeAuthRemover implements DozeMachine.Part { private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - public DozeAuthRemover(Context context) { - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); + @Inject + public DozeAuthRemover(KeyguardUpdateMonitor keyguardUpdateMonitor) { + mKeyguardUpdateMonitor = keyguardUpdateMonitor; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java index 554457b3564a..2a3d67fd7a8d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java @@ -22,33 +22,41 @@ import android.util.Log; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeMachine.State; +import com.android.systemui.doze.dagger.DozeScope; import java.io.PrintWriter; +import javax.inject.Inject; + /** * Handles dock events for ambient state changes. */ +@DozeScope public class DozeDockHandler implements DozeMachine.Part { private static final String TAG = "DozeDockHandler"; private static final boolean DEBUG = DozeService.DEBUG; private final AmbientDisplayConfiguration mConfig; - private final DozeMachine mMachine; + private DozeMachine mMachine; private final DockManager mDockManager; private final DockEventListener mDockEventListener; private int mDockState = DockManager.STATE_NONE; - DozeDockHandler(AmbientDisplayConfiguration config, DozeMachine machine, - DockManager dockManager) { - mMachine = machine; + @Inject + DozeDockHandler(AmbientDisplayConfiguration config, DockManager dockManager) { mConfig = config; mDockManager = dockManager; mDockEventListener = new DockEventListener(); } @Override + public void setDozeMachine(DozeMachine dozeMachine) { + mMachine = dozeMachine; + } + + @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { switch (newState) { case INITIALIZED: diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java deleted file mode 100644 index 3bac196ca59f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.doze; - -import android.annotation.Nullable; -import android.app.AlarmManager; -import android.app.IWallpaperManager; -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorManager; -import android.hardware.display.AmbientDisplayConfiguration; -import android.os.Handler; - -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.R; -import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dock.DockManager; -import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.statusbar.phone.BiometricUnlockController; -import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.util.sensors.AsyncSensorManager; -import com.android.systemui.util.sensors.ProximitySensor; -import com.android.systemui.util.wakelock.DelayedWakeLock; -import com.android.systemui.util.wakelock.WakeLock; - -import javax.inject.Inject; - -public class DozeFactory { - - private final FalsingManager mFalsingManager; - private final DozeLog mDozeLog; - private final DozeParameters mDozeParameters; - private final BatteryController mBatteryController; - private final AsyncSensorManager mAsyncSensorManager; - private final AlarmManager mAlarmManager; - private final WakefulnessLifecycle mWakefulnessLifecycle; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final DockManager mDockManager; - private final IWallpaperManager mWallpaperManager; - private final ProximitySensor mProximitySensor; - private final ProximitySensor.ProximityCheck mProximityCheck; - private final DelayedWakeLock.Builder mDelayedWakeLockBuilder; - private final Handler mHandler; - private final BiometricUnlockController mBiometricUnlockController; - private final BroadcastDispatcher mBroadcastDispatcher; - private final DozeHost mDozeHost; - - @Inject - public DozeFactory(FalsingManager falsingManager, DozeLog dozeLog, - DozeParameters dozeParameters, BatteryController batteryController, - AsyncSensorManager asyncSensorManager, AlarmManager alarmManager, - WakefulnessLifecycle wakefulnessLifecycle, KeyguardUpdateMonitor keyguardUpdateMonitor, - DockManager dockManager, @Nullable IWallpaperManager wallpaperManager, - ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proximityCheck, - DelayedWakeLock.Builder delayedWakeLockBuilder, @Main Handler handler, - BiometricUnlockController biometricUnlockController, - BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) { - mFalsingManager = falsingManager; - mDozeLog = dozeLog; - mDozeParameters = dozeParameters; - mBatteryController = batteryController; - mAsyncSensorManager = asyncSensorManager; - mAlarmManager = alarmManager; - mWakefulnessLifecycle = wakefulnessLifecycle; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mDockManager = dockManager; - mWallpaperManager = wallpaperManager; - mProximitySensor = proximitySensor; - mProximityCheck = proximityCheck; - mDelayedWakeLockBuilder = delayedWakeLockBuilder; - mHandler = handler; - mBiometricUnlockController = biometricUnlockController; - mBroadcastDispatcher = broadcastDispatcher; - mDozeHost = dozeHost; - } - - /** Creates a DozeMachine with its parts for {@code dozeService}. */ - DozeMachine assembleMachine(DozeService dozeService) { - AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(dozeService); - WakeLock wakeLock = mDelayedWakeLockBuilder.setHandler(mHandler).setTag("Doze").build(); - - DozeMachine.Service wrappedService = dozeService; - wrappedService = new DozeBrightnessHostForwarder(wrappedService, mDozeHost); - wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded( - wrappedService, mDozeParameters); - wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded( - wrappedService, mDozeParameters); - - DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock, - mWakefulnessLifecycle, mBatteryController, mDozeLog, mDockManager, - mDozeHost); - machine.setParts(new DozeMachine.Part[]{ - new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()), - new DozeFalsingManagerAdapter(mFalsingManager), - createDozeTriggers(dozeService, mAsyncSensorManager, mDozeHost, - mAlarmManager, config, mDozeParameters, wakeLock, - machine, mDockManager, mDozeLog, mProximityCheck), - createDozeUi(dozeService, mDozeHost, wakeLock, machine, mHandler, - mAlarmManager, mDozeParameters, mDozeLog), - new DozeScreenState(wrappedService, mHandler, mDozeHost, mDozeParameters, - wakeLock), - createDozeScreenBrightness(dozeService, wrappedService, mAsyncSensorManager, - mDozeHost, mDozeParameters, mHandler), - new DozeWallpaperState(mWallpaperManager, mBiometricUnlockController, - mDozeParameters), - new DozeDockHandler(config, machine, mDockManager), - new DozeAuthRemover(dozeService) - }); - - return machine; - } - - private DozeMachine.Part createDozeScreenBrightness(Context context, - DozeMachine.Service service, SensorManager sensorManager, DozeHost host, - DozeParameters params, Handler handler) { - Sensor sensor = DozeSensors.findSensorWithType(sensorManager, - context.getString(R.string.doze_brightness_sensor_type)); - return new DozeScreenBrightness(context, service, sensorManager, sensor, - mBroadcastDispatcher, host, handler, params.getPolicy()); - } - - private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager, - DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config, - DozeParameters params, WakeLock wakeLock, - DozeMachine machine, DockManager dockManager, DozeLog dozeLog, - ProximitySensor.ProximityCheck proximityCheck) { - boolean allowPulseTriggers = true; - return new DozeTriggers(context, machine, host, alarmManager, config, params, - sensorManager, wakeLock, allowPulseTriggers, dockManager, - mProximitySensor, proximityCheck, dozeLog, mBroadcastDispatcher); - - } - - private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock, - DozeMachine machine, Handler handler, AlarmManager alarmManager, - DozeParameters params, DozeLog dozeLog) { - return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params, - mKeyguardUpdateMonitor, dozeLog); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java index 250308d4c3a6..94b8ba305d0c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java @@ -16,15 +16,20 @@ package com.android.systemui.doze; +import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.plugins.FalsingManager; +import javax.inject.Inject; + /** * Notifies FalsingManager of whether or not AOD is showing. */ +@DozeScope public class DozeFalsingManagerAdapter implements DozeMachine.Part { private final FalsingManager mFalsingManager; + @Inject public DozeFalsingManagerAdapter(FalsingManager falsingManager) { mFalsingManager = falsingManager; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index ae7d82ac4a5e..b9d23ade2ee1 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -28,6 +28,8 @@ import android.view.Display; import com.android.internal.util.Preconditions; import com.android.systemui.dock.DockManager; +import com.android.systemui.doze.dagger.DozeScope; +import com.android.systemui.doze.dagger.WrappedService; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness; import com.android.systemui.statusbar.phone.DozeParameters; @@ -38,6 +40,8 @@ import com.android.systemui.util.wakelock.WakeLock; import java.io.PrintWriter; import java.util.ArrayList; +import javax.inject.Inject; + /** * Orchestrates all things doze. * @@ -46,6 +50,7 @@ import java.util.ArrayList; * * During state transitions and in certain states, DozeMachine holds a wake lock. */ +@DozeScope public class DozeMachine { static final String TAG = "DozeMachine"; @@ -146,9 +151,11 @@ public class DozeMachine { private boolean mWakeLockHeldForCurrentState = false; private DockManager mDockManager; - public DozeMachine(Service service, AmbientDisplayConfiguration config, WakeLock wakeLock, - WakefulnessLifecycle wakefulnessLifecycle, BatteryController batteryController, - DozeLog dozeLog, DockManager dockManager, DozeHost dozeHost) { + @Inject + public DozeMachine(@WrappedService Service service, AmbientDisplayConfiguration config, + WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle, + BatteryController batteryController, DozeLog dozeLog, DockManager dockManager, + DozeHost dozeHost, Part[] parts) { mDozeService = service; mConfig = config; mWakefulnessLifecycle = wakefulnessLifecycle; @@ -157,6 +164,10 @@ public class DozeMachine { mDozeLog = dozeLog; mDockManager = dockManager; mDozeHost = dozeHost; + mParts = parts; + for (Part part : parts) { + part.setDozeMachine(this); + } } /** @@ -168,12 +179,6 @@ public class DozeMachine { } } - /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */ - public void setParts(Part[] parts) { - Preconditions.checkState(mParts == null); - mParts = parts; - } - /** * Requests transitioning to {@code requestedState}. * @@ -432,6 +437,9 @@ public class DozeMachine { /** Alerts that the screenstate is being changed. */ default void onScreenState(int state) {} + + /** Sets the {@link DozeMachine} when this Part is associated with one. */ + default void setDozeMachine(DozeMachine dozeMachine) {} } /** A wrapper interface for {@link android.service.dreams.DreamService} */ diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java index 58f144830650..2b4067865415 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java @@ -19,25 +19,35 @@ package com.android.systemui.doze; import android.app.AlarmManager; import android.os.Handler; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.util.AlarmTimeout; +import javax.inject.Inject; + /** * Moves the doze machine from the pausing to the paused state after a timeout. */ +@DozeScope public class DozePauser implements DozeMachine.Part { public static final String TAG = DozePauser.class.getSimpleName(); private final AlarmTimeout mPauseTimeout; - private final DozeMachine mMachine; + private DozeMachine mMachine; private final AlwaysOnDisplayPolicy mPolicy; - public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager, + @Inject + public DozePauser(@Main Handler handler, AlarmManager alarmManager, AlwaysOnDisplayPolicy policy) { - mMachine = machine; mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler); mPolicy = policy; } @Override + public void setDozeMachine(DozeMachine dozeMachine) { + mMachine = dozeMachine; + } + + @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { switch (newState) { case DOZE_AOD_PAUSING: diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 64cfb4bcd058..342818de3d1e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -19,7 +19,6 @@ package com.android.systemui.doze; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -31,12 +30,17 @@ import android.os.UserHandle; import android.provider.Settings; import android.view.Display; -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.doze.dagger.BrightnessSensor; +import com.android.systemui.doze.dagger.DozeScope; +import com.android.systemui.doze.dagger.WrappedService; +import com.android.systemui.util.sensors.AsyncSensorManager; + +import javax.inject.Inject; /** * Controls the screen brightness when dozing. */ +@DozeScope public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part, SensorEventListener { private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties @@ -51,10 +55,8 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi private final Handler mHandler; private final SensorManager mSensorManager; private final Sensor mLightSensor; - private final BroadcastDispatcher mBroadcastDispatcher; private final int[] mSensorToBrightness; private final int[] mSensorToScrimOpacity; - private final boolean mDebuggable; private boolean mRegistered; private int mDefaultDozeBrightness; @@ -71,40 +73,20 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi private int mDebugBrightnessBucket = -1; private DozeMachine.State mState; - @VisibleForTesting - public DozeScreenBrightness(Context context, DozeMachine.Service service, - SensorManager sensorManager, Sensor lightSensor, - BroadcastDispatcher broadcastDispatcher, DozeHost host, - Handler handler, int defaultDozeBrightness, int[] sensorToBrightness, - int[] sensorToScrimOpacity, boolean debuggable) { + @Inject + public DozeScreenBrightness(Context context, @WrappedService DozeMachine.Service service, + AsyncSensorManager sensorManager, @BrightnessSensor Sensor lightSensor, + DozeHost host, Handler handler, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy) { mContext = context; mDozeService = service; mSensorManager = sensorManager; mLightSensor = lightSensor; - mBroadcastDispatcher = broadcastDispatcher; mDozeHost = host; mHandler = handler; - mDebuggable = debuggable; - - mDefaultDozeBrightness = defaultDozeBrightness; - mSensorToBrightness = sensorToBrightness; - mSensorToScrimOpacity = sensorToScrimOpacity; - - if (mDebuggable) { - IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_AOD_BRIGHTNESS); - mBroadcastDispatcher.registerReceiverWithHandler(this, filter, handler, UserHandle.ALL); - } - } - public DozeScreenBrightness(Context context, DozeMachine.Service service, - SensorManager sensorManager, Sensor lightSensor, - BroadcastDispatcher broadcastDispatcher, DozeHost host, Handler handler, - AlwaysOnDisplayPolicy policy) { - this(context, service, sensorManager, lightSensor, broadcastDispatcher, host, handler, - context.getResources().getInteger( - com.android.internal.R.integer.config_screenBrightnessDoze), - policy.screenBrightnessArray, policy.dimmingScrimArray, DEBUG_AOD_BRIGHTNESS); + mDefaultDozeBrightness = alwaysOnDisplayPolicy.defaultDozeBrightness; + mSensorToBrightness = alwaysOnDisplayPolicy.screenBrightnessArray; + mSensorToScrimOpacity = alwaysOnDisplayPolicy.dimmingScrimArray; } @Override @@ -139,9 +121,6 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi private void onDestroy() { setLightSensorEnabled(false); - if (mDebuggable) { - mBroadcastDispatcher.unregisterReceiver(this); - } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index 915359374bfe..8c50a16b566f 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -26,13 +26,19 @@ import android.os.Handler; import android.util.Log; import android.view.Display; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.doze.dagger.DozeScope; +import com.android.systemui.doze.dagger.WrappedService; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.wakelock.SettableWakeLock; import com.android.systemui.util.wakelock.WakeLock; +import javax.inject.Inject; + /** * Controls the screen when dozing. */ +@DozeScope public class DozeScreenState implements DozeMachine.Part { private static final boolean DEBUG = DozeService.DEBUG; @@ -59,8 +65,9 @@ public class DozeScreenState implements DozeMachine.Part { private int mPendingScreenState = Display.STATE_UNKNOWN; private SettableWakeLock mWakeLock; - public DozeScreenState(DozeMachine.Service service, Handler handler, DozeHost host, - DozeParameters parameters, WakeLock wakeLock) { + @Inject + public DozeScreenState(@WrappedService DozeMachine.Service service, @Main Handler handler, + DozeHost host, DozeParameters parameters, WakeLock wakeLock) { mDozeService = service; mHandler = handler; mParameters = parameters; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index aebf41b884c4..37bdda8a06a1 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -21,7 +21,6 @@ import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_ import android.annotation.AnyThread; import android.app.ActivityManager; -import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -64,10 +63,8 @@ public class DozeSensors { private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl(); private final Context mContext; - private final AlarmManager mAlarmManager; private final AsyncSensorManager mSensorManager; private final ContentResolver mResolver; - private final DozeParameters mDozeParameters; private final AmbientDisplayConfiguration mConfig; private final WakeLock mWakeLock; private final Consumer<Boolean> mProxCallback; @@ -98,14 +95,12 @@ public class DozeSensors { } } - public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager, + DozeSensors(Context context, AsyncSensorManager sensorManager, DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog, ProximitySensor proximitySensor) { mContext = context; - mAlarmManager = alarmManager; mSensorManager = sensorManager; - mDozeParameters = dozeParameters; mConfig = config; mWakeLock = wakeLock; mProxCallback = proxCallback; @@ -206,7 +201,10 @@ public class DozeSensors { return findSensorWithType(mSensorManager, type); } - static Sensor findSensorWithType(SensorManager sensorManager, String type) { + /** + * Utility method to find a {@link Sensor} for the supplied string type. + */ + public static Sensor findSensorWithType(SensorManager sensorManager, String type) { if (TextUtils.isEmpty(type)) { return null; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index d2bebb7b27d1..19b0ea1db04e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -22,6 +22,7 @@ import android.os.SystemClock; import android.service.dreams.DreamService; import android.util.Log; +import com.android.systemui.doze.dagger.DozeComponent; import com.android.systemui.plugins.DozeServicePlugin; import com.android.systemui.plugins.DozeServicePlugin.RequestDoze; import com.android.systemui.plugins.PluginListener; @@ -36,16 +37,16 @@ public class DozeService extends DreamService implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> { private static final String TAG = "DozeService"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private final DozeFactory mDozeFactory; + private final DozeComponent.Builder mDozeComponentBuilder; private DozeMachine mDozeMachine; private DozeServicePlugin mDozePlugin; private PluginManager mPluginManager; @Inject - public DozeService(DozeFactory dozeFactory, PluginManager pluginManager) { + public DozeService(DozeComponent.Builder dozeComponentBuilder, PluginManager pluginManager) { + mDozeComponentBuilder = dozeComponentBuilder; setDebug(DEBUG); - mDozeFactory = dozeFactory; mPluginManager = pluginManager; } @@ -56,7 +57,8 @@ public class DozeService extends DreamService setWindowless(true); mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */); - mDozeMachine = mDozeFactory.assembleMachine(this); + DozeComponent dozeComponent = mDozeComponentBuilder.build(this); + mDozeMachine = dozeComponent.getDozeMachine(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index eb2463b02ae4..0800a201bd92 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -41,6 +41,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; +import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.Assert; import com.android.systemui.util.sensors.AsyncSensorManager; @@ -51,9 +52,12 @@ import java.io.PrintWriter; import java.util.Optional; import java.util.function.Consumer; +import javax.inject.Inject; + /** * Handles triggers for ambient state changes. */ +@DozeScope public class DozeTriggers implements DozeMachine.Part { private static final String TAG = "DozeTriggers"; @@ -73,7 +77,7 @@ public class DozeTriggers implements DozeMachine.Part { private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500; private final Context mContext; - private final DozeMachine mMachine; + private DozeMachine mMachine; private final DozeLog mDozeLog; private final DozeSensors mDozeSensors; private final DozeHost mDozeHost; @@ -153,21 +157,21 @@ public class DozeTriggers implements DozeMachine.Part { } } - public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost, + @Inject + public DozeTriggers(Context context, DozeHost dozeHost, AlarmManager alarmManager, AmbientDisplayConfiguration config, DozeParameters dozeParameters, AsyncSensorManager sensorManager, - WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager, + WakeLock wakeLock, DockManager dockManager, ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck, DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) { mContext = context; - mMachine = machine; mDozeHost = dozeHost; mConfig = config; mDozeParameters = dozeParameters; mSensorManager = sensorManager; mWakeLock = wakeLock; - mAllowPulseTriggers = allowPulseTriggers; - mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters, + mAllowPulseTriggers = true; + mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters, config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor); mUiModeManager = mContext.getSystemService(UiModeManager.class); mDockManager = dockManager; @@ -177,6 +181,11 @@ public class DozeTriggers implements DozeMachine.Part { } @Override + public void setDozeMachine(DozeMachine dozeMachine) { + mMachine = dozeMachine; + } + + @Override public void destroy() { mDozeSensors.destroy(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 1c056215f1cb..0fdaae82e2d0 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -29,15 +29,20 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.WakeLock; import java.util.Calendar; +import javax.inject.Inject; + /** * The policy controlling doze. */ +@DozeScope public class DozeUi implements DozeMachine.Part { private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min @@ -45,7 +50,7 @@ public class DozeUi implements DozeMachine.Part { private final DozeHost mHost; private final Handler mHandler; private final WakeLock mWakeLock; - private final DozeMachine mMachine; + private DozeMachine mMachine; private final AlarmTimeout mTimeTicker; private final boolean mCanAnimateTransition; private final DozeParameters mDozeParameters; @@ -64,12 +69,12 @@ public class DozeUi implements DozeMachine.Part { private long mLastTimeTickElapsed = 0; - public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine, - WakeLock wakeLock, DozeHost host, Handler handler, + @Inject + public DozeUi(Context context, AlarmManager alarmManager, + WakeLock wakeLock, DozeHost host, @Main Handler handler, DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor, DozeLog dozeLog) { mContext = context; - mMachine = machine; mWakeLock = wakeLock; mHost = host; mHandler = handler; @@ -80,6 +85,11 @@ public class DozeUi implements DozeMachine.Part { mDozeLog = dozeLog; } + @Override + public void setDozeMachine(DozeMachine dozeMachine) { + mMachine = dozeMachine; + } + /** * Decide if we're taking over the screen-off animation * when the device was configured to skip doze after screen off. diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java index 7aeb7851bbd1..d5b6cb1a6250 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java @@ -21,15 +21,19 @@ import android.app.IWallpaperManager; import android.os.RemoteException; import android.util.Log; +import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; import java.io.PrintWriter; +import javax.inject.Inject; + /** * Propagates doze state to wallpaper engine. */ +@DozeScope public class DozeWallpaperState implements DozeMachine.Part { private static final String TAG = "DozeWallpaperState"; @@ -41,8 +45,9 @@ public class DozeWallpaperState implements DozeMachine.Part { private final BiometricUnlockController mBiometricUnlockController; private boolean mIsAmbientMode; + @Inject public DozeWallpaperState( - IWallpaperManager wallpaperManagerService, + @Nullable IWallpaperManager wallpaperManagerService, BiometricUnlockController biometricUnlockController, DozeParameters parameters) { mWallpaperManagerService = wallpaperManagerService; diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java new file mode 100644 index 000000000000..5af8af366b69 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.doze.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface BrightnessSensor { +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java new file mode 100644 index 000000000000..e925927e7564 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.doze.dagger; + +import com.android.systemui.doze.DozeMachine; +import com.android.systemui.doze.DozeService; + +import dagger.BindsInstance; +import dagger.Subcomponent; + +/** + * Dagger component for items that require a {@link DozeService}. + */ +@Subcomponent(modules = {DozeModule.class}) +@DozeScope +public interface DozeComponent { + /** Simple Builder for {@link DozeComponent}. */ + @Subcomponent.Factory + interface Builder { + DozeComponent build(@BindsInstance DozeService dozeService); + } + + /** Supply a {@link DozeMachine}. */ + @DozeScope + DozeMachine getDozeMachine(); +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java new file mode 100644 index 000000000000..a12e280fcca6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.doze.dagger; + +import android.content.Context; +import android.hardware.Sensor; +import android.os.Handler; + +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.doze.DozeAuthRemover; +import com.android.systemui.doze.DozeBrightnessHostForwarder; +import com.android.systemui.doze.DozeDockHandler; +import com.android.systemui.doze.DozeFalsingManagerAdapter; +import com.android.systemui.doze.DozeHost; +import com.android.systemui.doze.DozeMachine; +import com.android.systemui.doze.DozePauser; +import com.android.systemui.doze.DozeScreenBrightness; +import com.android.systemui.doze.DozeScreenState; +import com.android.systemui.doze.DozeScreenStatePreventingAdapter; +import com.android.systemui.doze.DozeSensors; +import com.android.systemui.doze.DozeService; +import com.android.systemui.doze.DozeSuspendScreenStatePreventingAdapter; +import com.android.systemui.doze.DozeTriggers; +import com.android.systemui.doze.DozeUi; +import com.android.systemui.doze.DozeWallpaperState; +import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.util.sensors.AsyncSensorManager; +import com.android.systemui.util.wakelock.DelayedWakeLock; +import com.android.systemui.util.wakelock.WakeLock; + +import dagger.Module; +import dagger.Provides; + +/** Dagger module for use with {@link com.android.systemui.doze.dagger.DozeComponent}. */ +@Module +public abstract class DozeModule { + @Provides + @DozeScope + @WrappedService + static DozeMachine.Service providesWrappedService(DozeService dozeService, DozeHost dozeHost, + DozeParameters dozeParameters) { + DozeMachine.Service wrappedService = dozeService; + wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost); + wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded( + wrappedService, dozeParameters); + wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded( + wrappedService, dozeParameters); + + return wrappedService; + } + + @Provides + @DozeScope + static WakeLock providesDozeWakeLock(DelayedWakeLock.Builder delayedWakeLockBuilder, + @Main Handler handler) { + return delayedWakeLockBuilder.setHandler(handler).setTag("Doze").build(); + } + + @Provides + static DozeMachine.Part[] providesDozeMachinePartes(DozePauser dozePauser, + DozeFalsingManagerAdapter dozeFalsingManagerAdapter, DozeTriggers dozeTriggers, + DozeUi dozeUi, DozeScreenState dozeScreenState, + DozeScreenBrightness dozeScreenBrightness, DozeWallpaperState dozeWallpaperState, + DozeDockHandler dozeDockHandler, DozeAuthRemover dozeAuthRemover) { + return new DozeMachine.Part[]{ + dozePauser, + dozeFalsingManagerAdapter, + dozeTriggers, + dozeUi, + dozeScreenState, + dozeScreenBrightness, + dozeWallpaperState, + dozeDockHandler, + dozeAuthRemover + }; + } + + @Provides + @BrightnessSensor + static Sensor providesBrightnessSensor(AsyncSensorManager sensorManager, Context context) { + return DozeSensors.findSensorWithType(sensorManager, + context.getString(R.string.doze_brightness_sensor_type)); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java new file mode 100644 index 000000000000..7a8b8166a969 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.doze.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Scope; + +/** + * Scope annotation for singleton items within the StatusBarComponent. + */ +@Documented +@Retention(RUNTIME) +@Scope +public @interface DozeScope {} diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java new file mode 100644 index 000000000000..ca8a158f21a2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.doze.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface WrappedService { +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt index e8f0e069c98d..d0642ccf9714 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt @@ -33,23 +33,31 @@ class MediaDataCombineLatest @Inject constructor( init { dataSource.addListener(object : MediaDataManager.Listener { override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - if (oldKey != null && !oldKey.equals(key)) { - val s = entries[oldKey]?.second - entries[key] = data to entries[oldKey]?.second - entries.remove(oldKey) + if (oldKey != null && oldKey != key && entries.contains(oldKey)) { + entries[key] = data to entries.remove(oldKey)?.second + update(key, oldKey) } else { entries[key] = data to entries[key]?.second + update(key, key) } - update(key, oldKey) } override fun onMediaDataRemoved(key: String) { remove(key) } }) deviceSource.addListener(object : MediaDeviceManager.Listener { - override fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) { - entries[key] = entries[key]?.first to data - update(key, key) + override fun onMediaDeviceChanged( + key: String, + oldKey: String?, + data: MediaDeviceData? + ) { + if (oldKey != null && oldKey != key && entries.contains(oldKey)) { + entries[key] = entries.remove(oldKey)?.first to data + update(key, oldKey) + } else { + entries[key] = entries[key]?.first to data + update(key, key) + } } override fun onKeyRemoved(key: String) { remove(key) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index 7ae2dc5c0941..143f8496e7aa 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -71,7 +71,8 @@ class MediaDeviceManager @Inject constructor( val controller = data.token?.let { MediaController(context, it) } - entry = Token(key, controller, localMediaManagerFactory.create(data.packageName)) + entry = Token(key, oldKey, controller, + localMediaManagerFactory.create(data.packageName)) entries[key] = entry entry.start() } @@ -98,23 +99,24 @@ class MediaDeviceManager @Inject constructor( } } - private fun processDevice(key: String, device: MediaDevice?) { + private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) { val enabled = device != null val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name) listeners.forEach { - it.onMediaDeviceChanged(key, data) + it.onMediaDeviceChanged(key, oldKey, data) } } interface Listener { /** Called when the route has changed for a given notification. */ - fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) + fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?) /** Called when the notification was removed. */ fun onKeyRemoved(key: String) } private inner class Token( val key: String, + val oldKey: String?, val controller: MediaController?, val localMediaManager: LocalMediaManager ) : LocalMediaManager.DeviceCallback { @@ -125,7 +127,7 @@ class MediaDeviceManager @Inject constructor( set(value) { if (!started || value != field) { field = value - processDevice(key, value) + processDevice(key, oldKey, value) } } fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java index 20ab114a97ec..1926c44abcba 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java @@ -19,11 +19,10 @@ package com.android.systemui.onehanded; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.IntDef; -import android.content.Context; import android.graphics.Rect; import android.view.SurfaceControl; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.view.animation.OvershootInterpolator; import androidx.annotation.VisibleForTesting; @@ -53,7 +52,7 @@ public class OneHandedAnimationController { public @interface TransitionDirection { } - private final Interpolator mFastOutSlowInInterpolator; + private final Interpolator mOvershootInterpolator; private final OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper; private final HashMap<SurfaceControl, OneHandedTransitionAnimator> mAnimatorMap = new HashMap<>(); @@ -62,11 +61,10 @@ public class OneHandedAnimationController { * Constructor of OneHandedAnimationController */ @Inject - public OneHandedAnimationController(Context context, + public OneHandedAnimationController( OneHandedSurfaceTransactionHelper surfaceTransactionHelper) { mSurfaceTransactionHelper = surfaceTransactionHelper; - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.fast_out_slow_in); + mOvershootInterpolator = new OvershootInterpolator(); } @SuppressWarnings("unchecked") @@ -104,7 +102,7 @@ public class OneHandedAnimationController { OneHandedTransitionAnimator setupOneHandedTransitionAnimator( OneHandedTransitionAnimator animator) { animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper); - animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setInterpolator(mOvershootInterpolator); animator.setFloatValues(FRACTION_START, FRACTION_END); return animator; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 673aa3903156..85a3bc91dc7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -142,7 +142,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { .expandableNotificationRow(row) .notificationEntry(entry) .onDismissRunnable(onDismissRunnable) - .rowContentBindStage(mRowContentBindStage) .onExpandClickListener(mPresenter) .build(); ExpandableNotificationRowController rowController = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java index 9846f2dcd170..321656df504a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java @@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; -import com.android.systemui.statusbar.notification.row.RowContentBindStage; import com.android.systemui.statusbar.phone.StatusBar; import dagger.Binds; @@ -57,8 +56,6 @@ public interface ExpandableNotificationRowComponent { @BindsInstance Builder onDismissRunnable(@DismissRunnable Runnable runnable); @BindsInstance - Builder rowContentBindStage(RowContentBindStage rowContentBindStage); - @BindsInstance Builder onExpandClickListener(ExpandableNotificationRow.OnExpandClickListener presenter); ExpandableNotificationRowComponent build(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index f5999f5c8294..f5ea1c880a41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -211,8 +211,4 @@ public class DozeParameters implements TunerService.Tunable, public void onTuningChanged(String key, String newValue) { mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT); } - - public AlwaysOnDisplayPolicy getPolicy() { - return mAlwaysOnPolicy; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index 3bd33ccca911..251693e162d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.policy; +import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; + import static com.android.settingslib.Utils.updateLocationEnabled; import android.app.ActivityManager; -import android.app.AppOpsManager; -import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -36,8 +36,9 @@ import android.provider.Settings; import androidx.annotation.VisibleForTesting; import com.android.systemui.BootCompleteCache; +import com.android.systemui.appops.AppOpItem; +import com.android.systemui.appops.AppOpsController; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.Utils; @@ -51,41 +52,32 @@ import javax.inject.Singleton; * A controller to manage changes of location related states and update the views accordingly. */ @Singleton -public class LocationControllerImpl extends BroadcastReceiver implements LocationController { - - private static final int[] mHighPowerRequestAppOpArray - = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION}; +public class LocationControllerImpl extends BroadcastReceiver implements LocationController, + AppOpsController.Callback { - private Context mContext; + private final Context mContext; + private final AppOpsController mAppOpsController; + private final BootCompleteCache mBootCompleteCache; + private final H mHandler; - private AppOpsManager mAppOpsManager; - private StatusBarManager mStatusBarManager; - private BroadcastDispatcher mBroadcastDispatcher; - private BootCompleteCache mBootCompleteCache; private boolean mAreActiveLocationRequests; - private final H mHandler; - @Inject - public LocationControllerImpl(Context context, @Main Looper mainLooper, - @Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher, + public LocationControllerImpl(Context context, AppOpsController appOpsController, + @Main Looper mainLooper, BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache) { mContext = context; - mBroadcastDispatcher = broadcastDispatcher; + mAppOpsController = appOpsController; mBootCompleteCache = bootCompleteCache; mHandler = new H(mainLooper); // Register to listen for changes in location settings. IntentFilter filter = new IntentFilter(); - filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); filter.addAction(LocationManager.MODE_CHANGED_ACTION); - mBroadcastDispatcher.registerReceiverWithHandler(this, filter, - new Handler(bgLooper), UserHandle.ALL); + broadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler, UserHandle.ALL); - mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - mStatusBarManager - = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); + mAppOpsController.addCallback(new int[]{OP_MONITOR_HIGH_POWER_LOCATION}, this); // Examine the current location state and initialize the status view. updateActiveLocationRequests(); @@ -160,27 +152,12 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio */ @VisibleForTesting protected boolean areActiveHighPowerLocationRequests() { - List<AppOpsManager.PackageOps> packages - = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray); - // AppOpsManager can return null when there is no requested data. - if (packages != null) { - final int numPackages = packages.size(); - for (int packageInd = 0; packageInd < numPackages; packageInd++) { - AppOpsManager.PackageOps packageOp = packages.get(packageInd); - List<AppOpsManager.OpEntry> opEntries = packageOp.getOps(); - if (opEntries != null) { - final int numOps = opEntries.size(); - for (int opInd = 0; opInd < numOps; opInd++) { - AppOpsManager.OpEntry opEntry = opEntries.get(opInd); - // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because - // of the mHighPowerRequestAppOpArray filter, but checking defensively. - if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { - if (opEntry.isRunning()) { - return true; - } - } - } - } + List<AppOpItem> appOpsItems = mAppOpsController.getActiveAppOps(); + + final int numItems = appOpsItems.size(); + for (int i = 0; i < numItems; i++) { + if (appOpsItems.get(i).getCode() == OP_MONITOR_HIGH_POWER_LOCATION) { + return true; } } @@ -198,14 +175,16 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio @Override public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) { - updateActiveLocationRequests(); - } else if (LocationManager.MODE_CHANGED_ACTION.equals(action)) { - mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED); + if (LocationManager.MODE_CHANGED_ACTION.equals(intent.getAction())) { + mHandler.locationSettingsChanged(); } } + @Override + public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { + updateActiveLocationRequests(); + } + private final class H extends Handler { private static final int MSG_LOCATION_SETTINGS_CHANGED = 1; private static final int MSG_LOCATION_ACTIVE_CHANGED = 2; diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index 3078e19c54f9..38b20c030116 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -226,6 +226,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (!activeControl.getSurfacePosition().equals(lastSurfacePosition) && mAnimation != null) { startAnimation(mImeShowing, true /* forceRestart */); + } else if (!mImeShowing) { + removeImeSurface(); } }); } @@ -375,16 +377,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged dispatchEndPositioning(mDisplayId, mCancelled, t); if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) { t.hide(mImeSourceControl.getLeash()); - final IInputMethodManager imms = getImms(); - if (imms != null) { - try { - // Remove the IME surface to make the insets invisible for - // non-client controlled insets. - imms.removeImeSurface(); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to remove IME surface.", e); - } - } + removeImeSurface(); } t.apply(); mTransactionPool.release(t); @@ -407,6 +400,19 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } + void removeImeSurface() { + final IInputMethodManager imms = getImms(); + if (imms != null) { + try { + // Remove the IME surface to make the insets invisible for + // non-client controlled insets. + imms.removeImeSurface(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to remove IME surface.", e); + } + } + } + /** * Allows other things to synchronize with the ime position */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java index 00d333fb593b..c591c1bd42bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java @@ -16,13 +16,13 @@ package com.android.systemui.doze; -import android.hardware.display.AmbientDisplayConfiguration; - import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; +import android.hardware.display.AmbientDisplayConfiguration; + import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.sensors.FakeSensorManager; @@ -37,7 +37,6 @@ public class DozeConfigurationUtil { when(params.getPulseOnSigMotion()).thenReturn(false); when(params.getPickupVibrationThreshold()).thenReturn(0); when(params.getProxCheckBeforePulse()).thenReturn(true); - when(params.getPolicy()).thenReturn(mock(AlwaysOnDisplayPolicy.class)); when(params.doubleTapReportsTouchCoordinates()).thenReturn(false); when(params.getDisplayNeedsBlanking()).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java index dc027997578f..5c2b153bf452 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java @@ -57,7 +57,8 @@ public class DozeDockHandlerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mConfig = DozeConfigurationUtil.createMockConfig(); mDockManagerFake = spy(new DockManagerFake()); - mDockHandler = new DozeDockHandler(mConfig, mMachine, mDockManagerFake); + mDockHandler = new DozeDockHandler(mConfig, mDockManagerFake); + mDockHandler.setDozeMachine(mMachine); when(mMachine.getState()).thenReturn(State.DOZE_AOD); doReturn(true).when(mConfig).alwaysOnEnabled(anyInt()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index 1f07f46bf764..8078b6c8bda4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -88,8 +88,7 @@ public class DozeMachineTest extends SysuiTestCase { mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake, mWakefulnessLifecycle, mock(BatteryController.class), mDozeLog, mDockManager, - mHost); - mMachine.setParts(new DozeMachine.Part[]{mPartMock}); + mHost, new DozeMachine.Part[]{mPartMock}); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 3ef60274cd76..3e60e016a0a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -40,14 +40,17 @@ import android.content.Intent; import android.os.PowerManager; import android.os.UserHandle; import android.provider.Settings; +import android.testing.AndroidTestingRunner; import android.view.Display; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; -import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.concurrency.FakeThreadFactory; +import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.sensors.FakeSensorManager; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -55,22 +58,24 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RunWith(AndroidJUnit4.class) @SmallTest +@RunWith(AndroidTestingRunner.class) public class DozeScreenBrightnessTest extends SysuiTestCase { - static final int DEFAULT_BRIGHTNESS = 10; - static final int[] SENSOR_TO_BRIGHTNESS = new int[]{-1, 1, 2, 3, 4}; - static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0}; + private static final int DEFAULT_BRIGHTNESS = 10; + private static final int[] SENSOR_TO_BRIGHTNESS = new int[]{-1, 1, 2, 3, 4}; + private static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0}; - DozeServiceFake mServiceFake; - FakeSensorManager.FakeGenericSensor mSensor; - FakeSensorManager mSensorManager; + private DozeServiceFake mServiceFake; + private FakeSensorManager.FakeGenericSensor mSensor; + private AsyncSensorManager mSensorManager; + private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy; @Mock DozeHost mDozeHost; - @Mock - BroadcastDispatcher mBroadcastDispatcher; - DozeScreenBrightness mScreen; + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); + private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor); + + private DozeScreenBrightness mScreen; @Before public void setUp() throws Exception { @@ -83,12 +88,17 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { return null; }).when(mDozeHost).prepareForGentleSleep(any()); mServiceFake = new DozeServiceFake(); - mSensorManager = new FakeSensorManager(mContext); - mSensor = mSensorManager.getFakeLightSensor(); + FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext); + mSensorManager = new AsyncSensorManager(fakeSensorManager, mFakeThreadFactory, null); + + mAlwaysOnDisplayPolicy = new AlwaysOnDisplayPolicy(mContext); + mAlwaysOnDisplayPolicy.defaultDozeBrightness = DEFAULT_BRIGHTNESS; + mAlwaysOnDisplayPolicy.screenBrightnessArray = SENSOR_TO_BRIGHTNESS; + mAlwaysOnDisplayPolicy.dimmingScrimArray = SENSOR_TO_OPACITY; + mSensor = fakeSensorManager.getFakeLightSensor(); mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, - mSensor.getSensor(), mBroadcastDispatcher, mDozeHost, null /* handler */, - DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, - true /* debuggable */); + mSensor.getSensor(), mDozeHost, null /* handler */, + mAlwaysOnDisplayPolicy); mScreen.onScreenState(Display.STATE_ON); } @@ -106,6 +116,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(3); @@ -116,6 +127,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { public void testAod_usesDebugValue() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); + mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS); intent.putExtra(DozeScreenBrightness.BRIGHTNESS_BUCKET, 1); @@ -141,6 +154,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(1); @@ -153,9 +167,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Test public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, - null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */, - DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, - true /* debuggable */); + null /* sensor */, mDozeHost, null /* handler */, + mAlwaysOnDisplayPolicy); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE); reset(mDozeHost); @@ -174,6 +187,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE); mScreen.transitionTo(DOZE_PULSE_DONE, DOZE); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(1); @@ -183,9 +197,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Test public void testNullSensor() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, - null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */, - DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, - true /* debuggable */); + null /* sensor */, mDozeHost, null /* handler */, + mAlwaysOnDisplayPolicy); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); @@ -198,6 +211,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.transitionTo(DOZE_AOD, FINISH); + waitForSensorManager(); mSensor.sendSensorEvent(1); @@ -205,10 +219,11 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test - public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() throws Exception { + public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(1); mSensor.sendSensorEvent(0); @@ -222,6 +237,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(2); @@ -233,6 +249,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { reset(mDozeHost); mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(2); verify(mDozeHost).setAodDimmingScrim(eq(0f)); } @@ -242,6 +259,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(2); mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING); @@ -251,4 +269,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD); verify(mDozeHost).setAodDimmingScrim(eq(0f)); } + + private void waitForSensorManager() { + mFakeExecutor.runAllReady(); + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index ebd2c3afe646..7ebead8a33fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -152,7 +152,7 @@ public class DozeSensorsTest extends SysuiTestCase { private class TestableDozeSensors extends DozeSensors { TestableDozeSensors() { - super(getContext(), mAlarmManager, mSensorManager, mDozeParameters, + super(getContext(), mSensorManager, mDozeParameters, mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog, mProximitySensor); for (TriggerSensor sensor : mSensors) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index d259d3685ea6..d3af835873e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -97,9 +97,10 @@ public class DozeTriggersTest extends SysuiTestCase { thresholdSensor.setLoaded(true); mProximitySensor = new FakeProximitySensor(thresholdSensor, null, mExecutor); - mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters, - asyncSensorManager, wakeLock, true, mDockManager, mProximitySensor, + mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters, + asyncSensorManager, wakeLock, mDockManager, mProximitySensor, mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher); + mTriggers.setDozeMachine(mMachine); waitForSensorManager(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java index c5bddc1f096f..069699c271f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java @@ -81,8 +81,9 @@ public class DozeUiTest extends SysuiTestCase { mWakeLock = new WakeLockFake(); mHandler = mHandlerThread.getThreadHandler(); - mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, + mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler, mDozeParameters, mKeyguardUpdateMonitor, mDozeLog); + mDozeUi.setDozeMachine(mMachine); } @After @@ -136,8 +137,9 @@ public class DozeUiTest extends SysuiTestCase { reset(mDozeParameters); reset(mHost); when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true); - mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, + mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler, mDozeParameters, mKeyguardUpdateMonitor, mDozeLog); + mDozeUi.setDozeMachine(mMachine); // Never animate if display doesn't support it. mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index dbc5596d9f4e..492b33e3c4a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import android.graphics.Color; @@ -47,6 +48,7 @@ import java.util.Map; public class MediaDataCombineLatestTest extends SysuiTestCase { private static final String KEY = "TEST_KEY"; + private static final String OLD_KEY = "TEST_KEY_OLD"; private static final String APP = "APP"; private static final String PACKAGE = "PKG"; private static final int BG_COLOR = Color.RED; @@ -97,7 +99,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void eventNotEmittedWithoutMedia() { // WHEN device source emits an event without media data - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN an event isn't emitted verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any()); } @@ -105,7 +107,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void emitEventAfterDeviceFirst() { // GIVEN that a device event has already been received - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN media event is received mDataListener.onMediaDataLoaded(KEY, null, mMediaData); // THEN the listener receives a combined event @@ -119,7 +121,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { // GIVEN that media event has already been received mDataListener.onMediaDataLoaded(KEY, null, mMediaData); // WHEN device event is received - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture()); @@ -127,6 +129,64 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { } @Test + public void migrateKeyMediaFirst() { + // GIVEN that media and device info has already been received + mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + reset(mListener); + // WHEN a key migration event is received + mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + // THEN the listener receives a combined event + ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture()); + assertThat(captor.getValue().getDevice()).isNotNull(); + } + + @Test + public void migrateKeyDeviceFirst() { + // GIVEN that media and device info has already been received + mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + reset(mListener); + // WHEN a key migration event is received + mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + // THEN the listener receives a combined event + ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture()); + assertThat(captor.getValue().getDevice()).isNotNull(); + } + + @Test + public void migrateKeyMediaAfter() { + // GIVEN that media and device info has already been received + mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + reset(mListener); + // WHEN a second key migration event is received for media + mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + // THEN the key has already been migrated + ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture()); + assertThat(captor.getValue().getDevice()).isNotNull(); + } + + @Test + public void migrateKeyDeviceAfter() { + // GIVEN that media and device info has already been received + mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + reset(mListener); + // WHEN a second key migration event is received for the device + mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + // THEN the key has already be migrated + ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture()); + assertThat(captor.getValue().getDevice()).isNotNull(); + } + + @Test public void mediaDataRemoved() { // WHEN media data is removed without first receiving device or data mDataListener.onMediaDataRemoved(KEY); @@ -143,7 +203,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void mediaDataRemovedAfterDeviceEvent() { - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); mDataListener.onMediaDataRemoved(KEY); verify(mListener).onMediaDataRemoved(eq(KEY)); } @@ -152,7 +212,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { public void mediaDataKeyUpdated() { // GIVEN that device and media events have already been received mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN the key is changed mDataListener.onMediaDataLoaded("NEW_KEY", KEY, mMediaData); // THEN the listener gets a load event with the correct keys @@ -163,7 +223,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void getDataIncludesDevice() { // GIVEN that device and media events have been received - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); mDataListener.onMediaDataLoaded(KEY, null, mMediaData); // THEN the result of getData includes device info diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt index fc22eeb3ea68..3c6e19f0ec6f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt @@ -166,7 +166,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { // THEN the listener for the old key should removed. verify(lmm).unregisterCallback(any()) // AND a new device event emitted - val data = captureDeviceData(KEY) + val data = captureDeviceData(KEY, KEY_OLD) assertThat(data.enabled).isTrue() assertThat(data.name).isEqualTo(DEVICE_NAME) } @@ -179,13 +179,14 @@ public class MediaDeviceManagerTest : SysuiTestCase() { // WHEN the new key is the same as the old key manager.onMediaDataLoaded(KEY, KEY, mediaData) // THEN no event should be emitted - verify(listener, never()).onMediaDeviceChanged(eq(KEY), any()) + verify(listener, never()).onMediaDeviceChanged(eq(KEY), eq(null), any()) } @Test fun unknownOldKey() { - manager.onMediaDataLoaded(KEY, "unknown", mediaData) - verify(listener).onMediaDeviceChanged(eq(KEY), any()) + val oldKey = "unknown" + manager.onMediaDataLoaded(KEY, oldKey, mediaData) + verify(listener).onMediaDeviceChanged(eq(KEY), eq(oldKey), any()) } @Test @@ -223,7 +224,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { manager.removeListener(listener) // THEN it doesn't receive device events manager.onMediaDataLoaded(KEY, null, mediaData) - verify(listener, never()).onMediaDeviceChanged(eq(KEY), any()) + verify(listener, never()).onMediaDeviceChanged(eq(KEY), eq(null), any()) } @Test @@ -318,9 +319,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() { return captor.getValue() } - fun captureDeviceData(key: String): MediaDeviceData { + fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData { val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java) - verify(listener).onMediaDeviceChanged(eq(key), captor.capture()) + verify(listener).onMediaDeviceChanged(eq(key), eq(oldKey), captor.capture()) return captor.getValue() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java index 86f4414009b6..583d0692565f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java @@ -52,7 +52,7 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mOneHandedAnimationController = new OneHandedAnimationController(mContext, + mOneHandedAnimationController = new OneHandedAnimationController( new OneHandedSurfaceTransactionHelper(mContext)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java index 7d4700f5f38a..b6b2217837b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java @@ -82,7 +82,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { final OneHandedSurfaceTransactionHelper transactionHelper = new OneHandedSurfaceTransactionHelper(mContext); final OneHandedAnimationController animationController = new OneHandedAnimationController( - mContext, transactionHelper); + transactionHelper); OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer( mContext, mMockDisplayController, animationController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index dd747871b1a5..c5374b2eb049 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -87,6 +87,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; @@ -121,8 +122,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { @Mock private NotificationGutsManager mGutsManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private NotificationMediaManager mNotificationMediaManager; - @Mock private ExpandableNotificationRowComponent.Builder - mExpandableNotificationRowComponentBuilder; + @Mock(answer = Answers.RETURNS_SELF) + private ExpandableNotificationRowComponent.Builder mExpandableNotificationRowComponentBuilder; @Mock private ExpandableNotificationRowComponent mExpandableNotificationRowComponent; @Mock private FalsingManager mFalsingManager; @Mock private KeyguardBypassController mKeyguardBypassController; @@ -211,21 +212,9 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { when(mExpandableNotificationRowComponentBuilder .expandableNotificationRow(viewCaptor.capture())) .thenReturn(mExpandableNotificationRowComponentBuilder); - when(mExpandableNotificationRowComponentBuilder - .notificationEntry(any())) - .thenReturn(mExpandableNotificationRowComponentBuilder); - when(mExpandableNotificationRowComponentBuilder - .onDismissRunnable(any())) - .thenReturn(mExpandableNotificationRowComponentBuilder); - when(mExpandableNotificationRowComponentBuilder - .rowContentBindStage(any())) - .thenReturn(mExpandableNotificationRowComponentBuilder); - when(mExpandableNotificationRowComponentBuilder - .onExpandClickListener(any())) - .thenReturn(mExpandableNotificationRowComponentBuilder); - when(mExpandableNotificationRowComponentBuilder.build()) .thenReturn(mExpandableNotificationRowComponent); + when(mExpandableNotificationRowComponent.getExpandableNotificationRowController()) .thenAnswer((Answer<ExpandableNotificationRowController>) invocation -> new ExpandableNotificationRowController( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java index 5ce209b1f249..4d6922c02c41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java @@ -15,12 +15,13 @@ package com.android.systemui.statusbar.policy; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import android.app.AppOpsManager; import android.content.Intent; import android.location.LocationManager; import android.testing.AndroidTestingRunner; @@ -31,12 +32,15 @@ import androidx.test.filters.SmallTest; import com.android.systemui.BootCompleteCache; import com.android.systemui.SysuiTestCase; +import com.android.systemui.appops.AppOpsController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -46,11 +50,15 @@ public class LocationControllerImplTest extends SysuiTestCase { private LocationControllerImpl mLocationController; private TestableLooper mTestableLooper; + @Mock private AppOpsController mAppOpsController; + @Before public void setup() { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); mLocationController = spy(new LocationControllerImpl(mContext, - mTestableLooper.getLooper(), + mAppOpsController, mTestableLooper.getLooper(), mock(BroadcastDispatcher.class), mock(BootCompleteCache.class))); @@ -67,12 +75,12 @@ public class LocationControllerImplTest extends SysuiTestCase { mLocationController.addCallback(callback); mLocationController.addCallback(mock(LocationChangeCallback.class)); - when(mLocationController.areActiveHighPowerLocationRequests()).thenReturn(false); - mLocationController.onReceive(mContext, new Intent( - LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION)); - when(mLocationController.areActiveHighPowerLocationRequests()).thenReturn(true); - mLocationController.onReceive(mContext, new Intent( - LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION)); + doReturn(false).when(mLocationController).areActiveHighPowerLocationRequests(); + mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0, + "", false); + doReturn(true).when(mLocationController).areActiveHighPowerLocationRequests(); + mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0, + "", true); mTestableLooper.processAllMessages(); } @@ -107,11 +115,22 @@ public class LocationControllerImplTest extends SysuiTestCase { LocationChangeCallback callback = mock(LocationChangeCallback.class); mLocationController.addCallback(callback); + + mTestableLooper.processAllMessages(); + mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION)); mTestableLooper.processAllMessages(); verify(callback, times(2)).onLocationSettingsChanged(anyBoolean()); + + doReturn(true).when(mLocationController).areActiveHighPowerLocationRequests(); + mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0, + "", true); + + mTestableLooper.processAllMessages(); + + verify(callback, times(1)).onLocationActiveChanged(anyBoolean()); } @Test @@ -124,6 +143,8 @@ public class LocationControllerImplTest extends SysuiTestCase { verify(callback).onLocationSettingsChanged(anyBoolean()); mLocationController.removeCallback(callback); + mTestableLooper.processAllMessages(); + mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION)); mTestableLooper.processAllMessages(); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 18fb7a3144cf..84bd59b7730e 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -43,8 +43,6 @@ import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIE import static android.os.storage.OnObbStateChangeListener.MOUNTED; import static android.os.storage.OnObbStateChangeListener.UNMOUNTED; import static android.os.storage.StorageManager.PROP_FORCED_SCOPED_STORAGE_WHITELIST; -import static android.os.storage.StorageManager.PROP_FUSE; -import static android.os.storage.StorageManager.PROP_SETTINGS_FUSE; import static com.android.internal.util.XmlUtils.readIntAttribute; import static com.android.internal.util.XmlUtils.readLongAttribute; @@ -131,7 +129,6 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.DataUnit; -import android.util.FeatureFlagUtils; import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -233,13 +230,6 @@ class StorageManagerService extends IStorageManager.Stub */ private static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled"; - /** - * If {@code 1}, enables FuseDaemon to intercept file system ops. If {@code -1}, - * disables FuseDaemon. If {@code 0}, uses the default value from the build system. - */ - private static final String FUSE_ENABLED = "fuse_enabled"; - private static final boolean DEFAULT_FUSE_ENABLED = true; - @GuardedBy("mLock") private final Set<Integer> mFuseMountedUser = new ArraySet<>(); @@ -609,8 +599,6 @@ class StorageManagerService extends IStorageManager.Stub // Not guarded by a lock. private final StorageSessionController mStorageSessionController; - private final boolean mIsFuseEnabled; - private final boolean mVoldAppDataIsolationEnabled; @GuardedBy("mLock") @@ -926,7 +914,6 @@ class StorageManagerService extends IStorageManager.Stub DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, mContext.getMainExecutor(), (properties) -> { refreshIsolatedStorageSettings(); - refreshFuseSettings(); }); refreshIsolatedStorageSettings(); } @@ -993,27 +980,6 @@ class StorageManagerService extends IStorageManager.Stub } /** - * The most recent flag change takes precedence. Change fuse Settings flag if Device Config is - * changed. Settings flag change will in turn change fuse system property (persist.sys.fuse) - * whenever the user reboots. - */ - private void refreshFuseSettings() { - int isFuseEnabled = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, - FUSE_ENABLED, 0); - if (isFuseEnabled == 1) { - Slog.d(TAG, "Device Config flag for FUSE is enabled, turn Settings fuse flag on"); - SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX - + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "true"); - } else if (isFuseEnabled == -1) { - Slog.d(TAG, "Device Config flag for FUSE is disabled, turn Settings fuse flag off"); - SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX - + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "false"); - } - // else, keep the build config. - // This can be overridden by direct adjustment of persist.sys.fflag.override.settings_fuse - } - - /** * MediaProvider has a ton of code that makes assumptions about storage * paths never changing, so we outright kill them to pick up new state. */ @@ -1091,13 +1057,9 @@ class StorageManagerService extends IStorageManager.Stub final UserManager userManager = mContext.getSystemService(UserManager.class); final List<UserInfo> users = userManager.getUsers(); - if (mIsFuseEnabled) { - mStorageSessionController.onReset(mVold, () -> { - mHandler.removeCallbacksAndMessages(null); - }); - } else { - killMediaProvider(users); - } + mStorageSessionController.onReset(mVold, () -> { + mHandler.removeCallbacksAndMessages(null); + }); final int[] systemUnlockedUsers; synchronized (mLock) { @@ -1490,8 +1452,7 @@ class StorageManagerService extends IStorageManager.Stub final ActivityManagerInternal amInternal = LocalServices.getService(ActivityManagerInternal.class); - if (mIsFuseEnabled && vol.mountUserId >= 0 - && !amInternal.isUserRunning(vol.mountUserId, 0)) { + if (vol.mountUserId >= 0 && !amInternal.isUserRunning(vol.mountUserId, 0)) { Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user " + Integer.toString(vol.mountUserId) + " is no longer running."); return; @@ -1803,11 +1764,7 @@ class StorageManagerService extends IStorageManager.Stub SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString( SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true))); - // If there is no value in the property yet (first boot after data wipe), this value may be - // incorrect until #updateFusePropFromSettings where we set the correct value and reboot if - // different - mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED); - mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean( + mVoldAppDataIsolationEnabled = SystemProperties.getBoolean( ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); mContext = context; mResolver = mContext.getContentResolver(); @@ -1821,7 +1778,7 @@ class StorageManagerService extends IStorageManager.Stub // Add OBB Action Handler to StorageManagerService thread. mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper()); - mStorageSessionController = new StorageSessionController(mContext, mIsFuseEnabled); + mStorageSessionController = new StorageSessionController(mContext); mInstaller = new Installer(mContext); mInstaller.onStart(); @@ -1869,26 +1826,6 @@ class StorageManagerService extends IStorageManager.Stub PackageManager.FEATURE_AUTOMOTIVE); } - /** - * Checks if user changed the persistent settings_fuse flag from Settings UI - * and updates PROP_FUSE (reboots if changed). - */ - private void updateFusePropFromSettings() { - boolean settingsFuseFlag = SystemProperties.getBoolean(PROP_SETTINGS_FUSE, - DEFAULT_FUSE_ENABLED); - Slog.d(TAG, "FUSE flags. Settings: " + settingsFuseFlag - + ". Default: " + DEFAULT_FUSE_ENABLED); - - if (mIsFuseEnabled != settingsFuseFlag) { - Slog.i(TAG, "Toggling persist.sys.fuse to " + settingsFuseFlag); - // Set prop_fuse to match prop_settings_fuse because it is used by native daemons like - // init, zygote, installd and vold - SystemProperties.set(PROP_FUSE, Boolean.toString(settingsFuseFlag)); - // Then perform hard reboot to kick policy into place - mContext.getSystemService(PowerManager.class).reboot("fuse_prop"); - } - } - private void start() { connectStoraged(); connectVold(); @@ -1987,15 +1924,6 @@ class StorageManagerService extends IStorageManager.Stub if (provider != null) { mExternalStorageAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid); } - - if (!mIsFuseEnabled) { - try { - mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, - mAppOpsCallback); - mIAppOpsService.startWatchingMode(OP_LEGACY_STORAGE, null, mAppOpsCallback); - } catch (RemoteException e) { - } - } } private ProviderInfo getProviderInfo(String authority) { @@ -2071,7 +1999,6 @@ class StorageManagerService extends IStorageManager.Stub private void bootCompleted() { mBootCompleted = true; mHandler.obtainMessage(H_BOOT_COMPLETED).sendToTarget(); - updateFusePropFromSettings(); } private void handleBootCompleted() { @@ -4269,14 +4196,14 @@ class StorageManagerService extends IStorageManager.Stub return Zygote.MOUNT_EXTERNAL_NONE; } - if (mIsFuseEnabled && mStorageManagerInternal.isExternalStorageService(uid)) { + if (mStorageManagerInternal.isExternalStorageService(uid)) { // Determine if caller requires pass_through mount; note that we do this for // all processes that share a UID with MediaProvider; but this is fine, since // those processes anyway share the same rights as MediaProvider. return Zygote.MOUNT_EXTERNAL_PASS_THROUGH; } - if (mIsFuseEnabled && (mDownloadsAuthorityAppId == UserHandle.getAppId(uid) + if ((mDownloadsAuthorityAppId == UserHandle.getAppId(uid) || mExternalStorageAuthorityAppId == UserHandle.getAppId(uid))) { // DownloadManager can write in app-private directories on behalf of apps; // give it write access to Android/ @@ -4286,7 +4213,7 @@ class StorageManagerService extends IStorageManager.Stub final boolean hasMtp = mIPackageManager.checkUidPermission(ACCESS_MTP, uid) == PERMISSION_GRANTED; - if (mIsFuseEnabled && hasMtp) { + if (hasMtp) { ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName, 0, UserHandle.getUserId(uid)); if (ai != null && ai.isSignedWithPlatformKey()) { @@ -4749,27 +4676,25 @@ class StorageManagerService extends IStorageManager.Stub public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode) { final long token = Binder.clearCallingIdentity(); try { - if (mIsFuseEnabled) { - // When using FUSE, we may need to kill the app if the op changes - switch(code) { - case OP_REQUEST_INSTALL_PACKAGES: - // Always kill regardless of op change, to remount apps /storage + // When using FUSE, we may need to kill the app if the op changes + switch(code) { + case OP_REQUEST_INSTALL_PACKAGES: + // Always kill regardless of op change, to remount apps /storage + killAppForOpChange(code, uid); + return; + case OP_MANAGE_EXTERNAL_STORAGE: + if (mode != MODE_ALLOWED) { + // Only kill if op is denied, to lose external_storage gid + // Killing when op is granted to pickup the gid automatically, + // results in a bad UX, especially since the gid only gives access + // to unreliable volumes, USB OTGs that are rarely mounted. The app + // will get the external_storage gid on next organic restart. killAppForOpChange(code, uid); - return; - case OP_MANAGE_EXTERNAL_STORAGE: - if (mode != MODE_ALLOWED) { - // Only kill if op is denied, to lose external_storage gid - // Killing when op is granted to pickup the gid automatically, - // results in a bad UX, especially since the gid only gives access - // to unreliable volumes, USB OTGs that are rarely mounted. The app - // will get the external_storage gid on next organic restart. - killAppForOpChange(code, uid); - } - return; - case OP_LEGACY_STORAGE: - updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED); - return; - } + } + return; + case OP_LEGACY_STORAGE: + updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED); + return; } if (mode == MODE_ALLOWED && (code == OP_READ_EXTERNAL_STORAGE diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 4ff9eb11c142..72f29b431880 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -43,6 +43,7 @@ import android.os.IBinder; import android.os.IExternalVibratorService; import android.os.IVibratorService; import android.os.IVibratorStateListener; +import android.os.Looper; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; @@ -70,6 +71,7 @@ import android.util.proto.ProtoOutputStream; import android.view.InputDevice; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; @@ -134,7 +136,7 @@ public class VibratorService extends IVibratorService.Stub private final SparseArray<VibrationEffect> mFallbackEffects; private final SparseArray<Integer> mProcStatesCache = new SparseArray<>(); private final WorkSource mTmpWorkSource = new WorkSource(); - private final Handler mH = new Handler(); + private final Handler mH; private final Object mLock = new Object(); private final Context mContext; @@ -147,6 +149,7 @@ public class VibratorService extends IVibratorService.Stub private Vibrator mVibrator; private SettingsObserver mSettingObserver; + private final NativeWrapper mNativeWrapper; private volatile VibrateThread mThread; // mInputDeviceVibrators lock should be acquired after mLock, if both are @@ -208,7 +211,12 @@ public class VibratorService extends IVibratorService.Stub } }; - private class Vibration implements IBinder.DeathRecipient { + /** + * Holder for a vibration to be played. This class can be shared with native methods for + * hardware callback support. + */ + @VisibleForTesting + public final class Vibration implements IBinder.DeathRecipient { public final IBinder token; // Start time in CLOCK_BOOTTIME base. public final long startTime; @@ -248,9 +256,9 @@ public class VibratorService extends IVibratorService.Stub } } - // Called by native - @SuppressWarnings("unused") - private void onComplete() { + /** Callback for when vibration is complete, to be called by native. */ + @VisibleForTesting + public void onComplete() { synchronized (mLock) { if (this == mCurrentVibration) { doCancelVibrateLocked(); @@ -354,15 +362,23 @@ public class VibratorService extends IVibratorService.Stub } VibratorService(Context context) { - vibratorInit(); + this(context, new Injector()); + } + + @VisibleForTesting + VibratorService(Context context, Injector injector) { + mNativeWrapper = injector.getNativeWrapper(); + mH = injector.createHandler(Looper.myLooper()); + + mNativeWrapper.vibratorInit(); // Reset the hardware to a default state, in case this is a runtime // restart instead of a fresh boot. - vibratorOff(); + mNativeWrapper.vibratorOff(); - mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl(); - mSupportsExternalControl = vibratorSupportsExternalControl(); - mSupportedEffects = asList(vibratorGetSupportedEffects()); - mCapabilities = vibratorGetCapabilities(); + mSupportsAmplitudeControl = mNativeWrapper.vibratorSupportsAmplitudeControl(); + mSupportsExternalControl = mNativeWrapper.vibratorSupportsExternalControl(); + mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects()); + mCapabilities = mNativeWrapper.vibratorGetCapabilities(); mContext = context; PowerManager pm = context.getSystemService(PowerManager.class); @@ -419,7 +435,7 @@ public class VibratorService extends IVibratorService.Stub mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH)); mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH)); - ServiceManager.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); + injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); } private VibrationEffect createEffectFromResource(int resId) { @@ -642,7 +658,7 @@ public class VibratorService extends IVibratorService.Stub if (effect == null) { synchronized (mLock) { mAlwaysOnEffects.delete(alwaysOnId); - vibratorAlwaysOnDisable(alwaysOnId); + mNativeWrapper.vibratorAlwaysOnDisable(alwaysOnId); } } else { if (!verifyVibrationEffect(effect)) { @@ -1198,11 +1214,11 @@ public class VibratorService extends IVibratorService.Stub private void updateAlwaysOnLocked(int id, Vibration vib) { final int intensity = getCurrentIntensityLocked(vib); if (!shouldVibrate(vib, intensity)) { - vibratorAlwaysOnDisable(id); + mNativeWrapper.vibratorAlwaysOnDisable(id); } else { final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect; final int strength = intensityToEffectStrength(intensity); - vibratorAlwaysOnEnable(id, prebaked.getId(), strength); + mNativeWrapper.vibratorAlwaysOnEnable(id, prebaked.getId(), strength); } } @@ -1238,7 +1254,7 @@ public class VibratorService extends IVibratorService.Stub //synchronized (mInputDeviceVibrators) { // return !mInputDeviceVibrators.isEmpty() || vibratorExists(); //} - return vibratorExists(); + return mNativeWrapper.vibratorExists(); } private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) { @@ -1262,7 +1278,7 @@ public class VibratorService extends IVibratorService.Stub // Note: ordering is important here! Many haptic drivers will reset their // amplitude when enabled, so we always have to enable first, then set the // amplitude. - vibratorOn(millis); + mNativeWrapper.vibratorOn(millis); doVibratorSetAmplitude(amplitude); } } @@ -1273,7 +1289,7 @@ public class VibratorService extends IVibratorService.Stub private void doVibratorSetAmplitude(int amplitude) { if (mSupportsAmplitudeControl) { - vibratorSetAmplitude(amplitude); + mNativeWrapper.vibratorSetAmplitude(amplitude); } } @@ -1291,7 +1307,7 @@ public class VibratorService extends IVibratorService.Stub mInputDeviceVibrators.get(i).cancel(); } } else { - vibratorOff(); + mNativeWrapper.vibratorOff(); } } } finally { @@ -1310,7 +1326,7 @@ public class VibratorService extends IVibratorService.Stub } // Input devices don't support prebaked effect, so skip trying it with them. if (!usingInputDeviceVibrators) { - long duration = vibratorPerformEffect(prebaked.getId(), + long duration = mNativeWrapper.vibratorPerformEffect(prebaked.getId(), prebaked.getEffectStrength(), vib, hasCapability(IVibrator.CAP_PERFORM_CALLBACK)); long timeout = duration; @@ -1363,7 +1379,7 @@ public class VibratorService extends IVibratorService.Stub PrimitiveEffect[] primitiveEffects = composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]); - vibratorPerformComposedEffect(primitiveEffects, vib); + mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib); // Composed effects don't actually give us an estimated duration, so we just guess here. noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length); @@ -1454,7 +1470,7 @@ public class VibratorService extends IVibratorService.Stub } } mVibratorUnderExternalControl = externalControl; - vibratorSetExternalControl(externalControl); + mNativeWrapper.vibratorSetExternalControl(externalControl); } private void dumpInternal(PrintWriter pw) { @@ -1688,6 +1704,100 @@ public class VibratorService extends IVibratorService.Stub } } + /** Wrapper around the static-native methods of {@link VibratorService} for tests. */ + @VisibleForTesting + public static class NativeWrapper { + + /** Checks if vibrator exists on device. */ + public boolean vibratorExists() { + return VibratorService.vibratorExists(); + } + + /** Initializes connection to vibrator HAL service. */ + public void vibratorInit() { + VibratorService.vibratorInit(); + } + + /** Turns vibrator on for given time. */ + public void vibratorOn(long milliseconds) { + VibratorService.vibratorOn(milliseconds); + } + + /** Turns vibrator off. */ + public void vibratorOff() { + VibratorService.vibratorOff(); + } + + /** Returns true if vibrator supports {@link #vibratorSetAmplitude(int)}. */ + public boolean vibratorSupportsAmplitudeControl() { + return VibratorService.vibratorSupportsAmplitudeControl(); + } + + /** Sets the amplitude for the vibrator to run. */ + public void vibratorSetAmplitude(int amplitude) { + VibratorService.vibratorSetAmplitude(amplitude); + } + + /** Returns all predefined effects supported by the device vibrator. */ + public int[] vibratorGetSupportedEffects() { + return VibratorService.vibratorGetSupportedEffects(); + } + + /** Turns vibrator on to perform one of the supported effects. */ + public long vibratorPerformEffect(long effect, long strength, Vibration vibration, + boolean withCallback) { + return VibratorService.vibratorPerformEffect(effect, strength, vibration, withCallback); + } + + /** Turns vibrator on to perform one of the supported composed effects. */ + public void vibratorPerformComposedEffect( + VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) { + VibratorService.vibratorPerformComposedEffect(effect, vibration); + } + + /** Returns true if vibrator supports {@link #vibratorSetExternalControl(boolean)}. */ + public boolean vibratorSupportsExternalControl() { + return VibratorService.vibratorSupportsExternalControl(); + } + + /** Enabled the device vibrator to be controlled by another service. */ + public void vibratorSetExternalControl(boolean enabled) { + VibratorService.vibratorSetExternalControl(enabled); + } + + /** Returns all capabilities of the device vibrator. */ + public long vibratorGetCapabilities() { + return VibratorService.vibratorGetCapabilities(); + } + + /** Enable always-on vibration with given id and effect. */ + public void vibratorAlwaysOnEnable(long id, long effect, long strength) { + VibratorService.vibratorAlwaysOnEnable(id, effect, strength); + } + + /** Disable always-on vibration for given id. */ + public void vibratorAlwaysOnDisable(long id) { + VibratorService.vibratorAlwaysOnDisable(id); + } + } + + /** Point of injection for test dependencies */ + @VisibleForTesting + static class Injector { + + NativeWrapper getNativeWrapper() { + return new NativeWrapper(); + } + + Handler createHandler(Looper looper) { + return new Handler(looper); + } + + void addService(String name, IBinder service) { + ServiceManager.addService(name, service); + } + } + BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ce66b2cbf114..25e6eb6e943f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -232,7 +232,6 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Rect; import android.hardware.display.DisplayManagerInternal; -import android.location.LocationManager; import android.media.audiofx.AudioEffect; import android.net.Proxy; import android.net.Uri; @@ -15863,7 +15862,6 @@ public class ActivityManagerService extends IActivityManager.Stub || Intent.ACTION_FACTORY_RESET.equals(action) || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action) || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action) - || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action) || TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action) || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action) || AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action) diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 5124c4a4797e..a2eea1348d5c 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -470,22 +470,23 @@ public final class BroadcastQueue { // if this receiver was slow, impose deferral policy on the app. This will kick in // when processNextBroadcastLocked() next finds this uid as a receiver identity. if (!r.timeoutExempt) { - if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) { + // r.curApp can be null if finish has raced with process death - benign + // edge case, and we just ignore it because we're already cleaning up + // as expected. + if (r.curApp != null + && mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) { // Core system packages are exempt from deferral policy if (!UserHandle.isCore(r.curApp.uid)) { if (DEBUG_BROADCAST_DEFERRAL) { Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1) + " was slow: " + receiver + " br=" + r); } - if (r.curApp != null) { - mDispatcher.startDeferring(r.curApp.uid); - } else { - Slog.d(TAG_BROADCAST, "finish receiver curApp is null? " + r); - } + mDispatcher.startDeferring(r.curApp.uid); } else { if (DEBUG_BROADCAST_DEFERRAL) { Slog.i(TAG_BROADCAST, "Core uid " + r.curApp.uid - + " receiver was slow but not deferring: " + receiver + " br=" + r); + + " receiver was slow but not deferring: " + + receiver + " br=" + r); } } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 5a0ea7586301..ebff0691c1f7 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -110,7 +110,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ProcessMap; import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.os.RuntimeInit; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; @@ -155,9 +154,6 @@ public final class ProcessList { static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = "persist.sys.vold_app_data_isolation_enabled"; - // A system property to control if fuse is enabled. - static final String ANDROID_FUSE_ENABLED = "persist.sys.fuse"; - // The minimum time we allow between crashes, for us to consider this // application to be bad and stop and its services and reject broadcasts. static final int MIN_CRASH_INTERVAL = 60 * 1000; @@ -719,13 +715,8 @@ public final class ProcessList { // want some apps enabled while some apps disabled mAppDataIsolationEnabled = SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true); - boolean fuseEnabled = SystemProperties.getBoolean(ANDROID_FUSE_ENABLED, false); - boolean voldAppDataIsolationEnabled = SystemProperties.getBoolean( + mVoldAppDataIsolationEnabled = SystemProperties.getBoolean( ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); - if (!fuseEnabled && voldAppDataIsolationEnabled) { - Slog.e(TAG, "Fuse is not enabled while vold app data isolation is enabled"); - } - mVoldAppDataIsolationEnabled = fuseEnabled && voldAppDataIsolationEnabled; mAppDataIsolationWhitelistedApps = new ArrayList<>( SystemConfig.getInstance().getAppDataIsolationWhitelistedApps()); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 4fd9ae256592..c8e4ee02e603 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1320,6 +1320,11 @@ public class AudioService extends IAudioService.Stub device, caller, true /*hasModifyAudioSettings*/); } mStreamStates[streamType].checkFixedVolumeDevices(); + + // Unmute streams if device is full volume + if (mFullVolumeDevices.contains(device)) { + mStreamStates[streamType].mute(false); + } } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index 70f0399d1070..05cf40a091b6 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -120,6 +120,11 @@ public abstract class InputMethodManagerInternal { public abstract void reportImeControl(@Nullable IBinder windowToken); /** + * Destroys the IME surface. + */ + public abstract void removeImeSurface(); + + /** * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing. */ private static final InputMethodManagerInternal NOP = @@ -166,6 +171,10 @@ public abstract class InputMethodManagerInternal { @Override public void reportImeControl(@Nullable IBinder windowToken) { } + + @Override + public void removeImeSurface() { + } }; /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index c027ebcfd568..d8ee32e7bd74 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -211,6 +211,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_INITIALIZE_IME = 1040; static final int MSG_CREATE_SESSION = 1050; static final int MSG_REMOVE_IME_SURFACE = 1060; + static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061; static final int MSG_START_INPUT = 2000; @@ -4005,6 +4006,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE)); } + @Override + public void removeImeSurfaceFromWindow(IBinder windowToken) { + // No permission check, because we'll only execute the request if the calling window is + // also the current IME client. + mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget(); + } + @BinderThread private void notifyUserAction(@NonNull IBinder token) { if (DEBUG) { @@ -4278,11 +4286,27 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return true; } case MSG_REMOVE_IME_SURFACE: { - try { - if (mEnabledSession != null && mEnabledSession.session != null) { - mEnabledSession.session.removeImeSurface(); + synchronized (mMethodMap) { + try { + if (mEnabledSession != null && mEnabledSession.session != null + && !mShowRequested) { + mEnabledSession.session.removeImeSurface(); + } + } catch (RemoteException e) { + } + } + return true; + } + case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: { + IBinder windowToken = (IBinder) msg.obj; + synchronized (mMethodMap) { + try { + if (windowToken == mCurFocusedWindow + && mEnabledSession != null && mEnabledSession.session != null) { + mEnabledSession.session.removeImeSurface(); + } + } catch (RemoteException e) { } - } catch (RemoteException e) { } return true; } @@ -5116,6 +5140,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void reportImeControl(@Nullable IBinder windowToken) { mService.reportImeControl(windowToken); } + + @Override + public void removeImeSurface() { + mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE)); + } } @BinderThread diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 23392db0690d..2516e289f099 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -226,6 +226,11 @@ public final class MultiClientInputMethodManagerService { @Override public void reportImeControl(@Nullable IBinder windowToken) { } + + @Override + public void removeImeSurface() { + reportNotSupported(); + } }); } @@ -1482,6 +1487,12 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override + public void removeImeSurfaceFromWindow(IBinder windowToken) { + reportNotSupported(); + } + + @BinderThread + @Override public boolean showSoftInput( IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver) { diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 5c1d1826b88a..f69c8239762d 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -25,7 +25,6 @@ import static android.location.LocationManager.EXTRA_PROVIDER_ENABLED; import static android.location.LocationManager.EXTRA_PROVIDER_NAME; import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; -import static android.location.LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION; import static android.location.LocationManager.KEY_LOCATION_CHANGED; import static android.location.LocationManager.KEY_PROVIDER_ENABLED; import static android.location.LocationManager.MODE_CHANGED_ACTION; @@ -1222,21 +1221,10 @@ public class LocationManagerService extends ILocationManager.Stub { false); // Now update monitoring of high power requests only. - boolean wasHighPowerMonitoring = mOpHighPowerMonitoring; mOpHighPowerMonitoring = updateMonitoring( requestingHighPowerLocation, mOpHighPowerMonitoring, true); - if (mOpHighPowerMonitoring != wasHighPowerMonitoring) { - long identity = Binder.clearCallingIdentity(); - try { - // Send an intent to notify that a high power request has been added/removed. - Intent intent = new Intent(HIGH_POWER_REQUEST_CHANGE_ACTION); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } finally { - Binder.restoreCallingIdentity(identity); - } - } } private boolean updateMonitoring(boolean allowMonitoring, boolean currentlyMonitoring, diff --git a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java index 70d83a9dd7dd..631dbbf0f1fd 100644 --- a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java +++ b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java @@ -27,7 +27,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.location.LocationManager; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; @@ -582,17 +581,9 @@ class GnssVisibilityControl { mAppOps.finishOp(AppOpsManager.OP_MONITOR_LOCATION, uid, proxyAppPkgName); mAppOps.finishOp(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, uid, proxyAppPkgName); } - sendHighPowerMonitoringBroadcast(); return true; } - private void sendHighPowerMonitoringBroadcast() { - // Send an intent to notify that a high power request has been added/removed so that - // the SystemUi checks the state of AppOps and updates the location icon accordingly. - Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } - private void handleEmergencyNfwNotification(NfwNotification nfwNotification) { boolean isPermissionMismatched = false; if (!nfwNotification.isRequestAccepted()) { diff --git a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java index 78af79b533d8..a95383695ae8 100644 --- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java +++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java @@ -16,12 +16,8 @@ package com.android.server.location.util; -import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; - import android.app.AppOpsManager; import android.content.Context; -import android.content.Intent; -import android.location.LocationManager; import android.location.util.identity.CallerIdentity; import android.os.Binder; @@ -66,22 +62,13 @@ public class SystemAppOpsHelper extends AppOpsHelper { long identity = Binder.clearCallingIdentity(); try { - boolean allowed = mAppOps.startOpNoThrow( + return mAppOps.startOpNoThrow( appOp, callerIdentity.getUid(), callerIdentity.getPackageName(), false, callerIdentity.getAttributionTag(), callerIdentity.getListenerId()) == AppOpsManager.MODE_ALLOWED; - - if (allowed && appOp == OP_MONITOR_HIGH_POWER_LOCATION) { - // notify of possible location icon change - mContext.sendBroadcast( - new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION).addFlags( - Intent.FLAG_RECEIVER_FOREGROUND)); - } - - return allowed; } finally { Binder.restoreCallingIdentity(identity); } @@ -98,13 +85,6 @@ public class SystemAppOpsHelper extends AppOpsHelper { callerIdentity.getUid(), callerIdentity.getPackageName(), callerIdentity.getAttributionTag()); - - if (appOp == OP_MONITOR_HIGH_POWER_LOCATION) { - // notify of possible location icon change - mContext.sendBroadcast( - new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION).addFlags( - Intent.FLAG_RECEIVER_FOREGROUND)); - } } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 951f462ed201..592db83b8721 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -77,6 +77,8 @@ public abstract class ApexManager { public static final int MATCH_ACTIVE_PACKAGE = 1 << 0; static final int MATCH_FACTORY_PACKAGE = 1 << 1; + private static final String VNDK_APEX_MODULE_NAME_PREFIX = "com.android.vndk."; + private static final Singleton<ApexManager> sApexManagerSingleton = new Singleton<ApexManager>() { @Override @@ -342,8 +344,17 @@ public abstract class ApexManager { public abstract boolean destroyDeSnapshots(int rollbackId); /** + * Deletes snapshots of the credential encrypted apex data directories for the specified user, + * for the given rollback id as long as the user is credential unlocked. + * + * @return boolean true if the delete was successful + */ + public abstract boolean destroyCeSnapshots(int userId, int rollbackId); + + /** * Deletes snapshots of the credential encrypted apex data directories for the specified user, - * where the rollback id is not included in {@code retainRollbackIds}. + * where the rollback id is not included in {@code retainRollbackIds} as long as the user is + * credential unlocked. * * @return boolean true if the delete was successful */ @@ -526,7 +537,9 @@ public abstract class ApexManager { activePackagesSet.add(packageInfo.packageName); } if (ai.isFactory) { - if (factoryPackagesSet.contains(packageInfo.packageName)) { + // Don't throw when the duplicating APEX is VNDK APEX + if (factoryPackagesSet.contains(packageInfo.packageName) + && !ai.moduleName.startsWith(VNDK_APEX_MODULE_NAME_PREFIX)) { throw new IllegalStateException( "Two factory packages have the same name: " + packageInfo.packageName); @@ -875,6 +888,17 @@ public abstract class ApexManager { } @Override + public boolean destroyCeSnapshots(int userId, int rollbackId) { + try { + waitForApexService().destroyCeSnapshots(userId, rollbackId); + return true; + } catch (Exception e) { + Slog.e(TAG, e.getMessage(), e); + return false; + } + } + + @Override public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { try { waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds); @@ -1136,6 +1160,11 @@ public abstract class ApexManager { } @Override + public boolean destroyCeSnapshots(int userId, int rollbackId) { + return true; + } + + @Override public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { return true; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index ddc5e14bfeb3..312dcddd577d 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -84,6 +84,8 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemConfig; +import com.android.server.SystemService; +import com.android.server.SystemServiceManager; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -201,6 +203,27 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } }; + private static final class Lifecycle extends SystemService { + private final PackageInstallerService mPackageInstallerService; + + Lifecycle(Context context, PackageInstallerService service) { + super(context); + mPackageInstallerService = service; + } + + @Override + public void onStart() { + // no-op + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { + mPackageInstallerService.onBroadcastReady(); + } + } + } + public PackageInstallerService(Context context, PackageManagerService pm, Supplier<PackageParser2> apexParserSupplier) { mContext = context; @@ -222,6 +245,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mApexManager = ApexManager.getInstance(); mStagingManager = new StagingManager(this, context, apexParserSupplier); + + LocalServices.getService(SystemServiceManager.class).startService( + new Lifecycle(context, this)); } boolean okToSendBroadcasts() { @@ -259,6 +285,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } + private void onBroadcastReady() { + // Broadcasts are not sent while we restore sessions on boot, since no processes would be + // ready to listen to them. From now on, it is safe to send broadcasts which otherwise will + // be rejected by ActivityManagerService if its systemReady() is not completed. + mOkToSendBroadcasts = true; + } + void restoreAndApplyStagedSessionIfNeeded() { List<PackageInstallerSession> stagedSessionsToRestore = new ArrayList<>(); synchronized (mSessions) { @@ -281,16 +314,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } mStagingManager.restoreSession(session, isDeviceUpgrading); } - // Broadcasts are not sent while we restore sessions on boot, since no processes would be - // ready to listen to them. From now on, we greedily assume that broadcasts requests are - // safe to send out. The worst that can happen is that a broadcast is attempted before - // ActivityManagerService completes its own systemReady(), in which case it will be rejected - // with an otherwise harmless exception. - // A more appropriate way to do this would be to wait until the correct boot phase is - // reached, but since we are not a SystemService we can't override onBootPhase. - // Waiting on the BOOT_COMPLETED broadcast can take several minutes, so that's not a viable - // way either. - mOkToSendBroadcasts = true; } @GuardedBy("mSessions") @@ -592,12 +615,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } - if (mBypassNextStagedInstallerCheck) { - mBypassNextStagedInstallerCheck = false; - } else if (params.isStaged - && !isCalledBySystemOrShell(callingUid) - && !isWhitelistedStagedInstaller(requestedInstallerPackageName)) { - throw new SecurityException("Installer not allowed to commit staged install"); + if (params.isStaged && !isCalledBySystemOrShell(callingUid)) { + if (mBypassNextStagedInstallerCheck) { + mBypassNextStagedInstallerCheck = false; + } else if (!isStagedInstallerAllowed(requestedInstallerPackageName)) { + throw new SecurityException("Installer not allowed to commit staged install"); + } } if (!params.isMultiPackage) { @@ -729,7 +752,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements || callingUid == Process.SHELL_UID; } - private boolean isWhitelistedStagedInstaller(String installerName) { + private boolean isStagedInstallerAllowed(String installerName) { return SystemConfig.getInstance().getWhitelistedStagedInstallers().contains(installerName); } diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java index 88938b204f6c..c8e36481c738 100644 --- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java +++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java @@ -206,6 +206,16 @@ public class AppDataRollbackHelper { } /** + * Deletes snapshots of the credential encrypted apex data directories for the specified user, + * for the given rollback id. This method will be a no-op if the user is not unlocked. + */ + public void destroyApexCeSnapshots(int userId, int rollbackId) { + if (!isUserCredentialLocked(userId)) { + mApexManager.destroyCeSnapshots(userId, rollbackId); + } + } + + /** * Commits the pending backups and restores for a given {@code userId} and {@code rollback}. If * the rollback has a pending backup, it is updated with a mapping from {@code userId} to inode * of the CE user data snapshot. diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index c3477804fb64..2db6043f1c0a 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -42,6 +42,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.ext.SdkExtensions; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Slog; import android.util.SparseIntArray; @@ -61,9 +62,9 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.function.Consumer; - /** * Information about a rollback available for a set of atomically installed packages. * @@ -699,11 +700,13 @@ class Rollback { void delete(AppDataRollbackHelper dataHelper) { assertInWorkerThread(); boolean containsApex = false; + Set<Integer> apexUsers = new ArraySet<>(); for (PackageRollbackInfo pkgInfo : info.getPackages()) { + List<Integer> snapshottedUsers = pkgInfo.getSnapshottedUsers(); if (pkgInfo.isApex()) { containsApex = true; + apexUsers.addAll(snapshottedUsers); } else { - List<Integer> snapshottedUsers = pkgInfo.getSnapshottedUsers(); for (int i = 0; i < snapshottedUsers.size(); i++) { // Destroy app data snapshot. int userId = snapshottedUsers.get(i); @@ -714,6 +717,9 @@ class Rollback { } if (containsApex) { dataHelper.destroyApexDeSnapshots(info.getRollbackId()); + for (int user : apexUsers) { + dataHelper.destroyApexCeSnapshots(user, info.getRollbackId()); + } } RollbackStore.deleteRollback(this); diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java index 37df5481d3c4..6dc1d6921dbb 100644 --- a/services/core/java/com/android/server/storage/StorageSessionController.java +++ b/services/core/java/com/android/server/storage/StorageSessionController.java @@ -53,16 +53,14 @@ public final class StorageSessionController { private final Context mContext; @GuardedBy("mLock") private final SparseArray<StorageUserConnection> mConnections = new SparseArray<>(); - private final boolean mIsFuseEnabled; private volatile ComponentName mExternalStorageServiceComponent; private volatile String mExternalStorageServicePackageName; private volatile int mExternalStorageServiceAppId; private volatile boolean mIsResetting; - public StorageSessionController(Context context, boolean isFuseEnabled) { + public StorageSessionController(Context context) { mContext = Objects.requireNonNull(context); - mIsFuseEnabled = isFuseEnabled; } /** @@ -361,6 +359,6 @@ public final class StorageSessionController { } private boolean shouldHandle(@Nullable VolumeInfo vol) { - return mIsFuseEnabled && !mIsResetting && (vol == null || isEmulatedOrPublic(vol)); + return !mIsResetting && (vol == null || isEmulatedOrPublic(vol)); } } diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 5f6323369d0a..f5eed30a19bf 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -114,7 +114,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { private static final boolean DEBUG = false; private static final String TAG = "UriGrantsManagerService"; // Maximum number of persisted Uri grants a package is allowed - private static final int MAX_PERSISTED_URI_GRANTS = 128; + private static final int MAX_PERSISTED_URI_GRANTS = 512; private static final boolean ENABLE_DYNAMIC_PERMISSIONS = true; private final Object mLock = new Object(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 57e696320ec8..4e1d789bebd8 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -4006,8 +4006,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { ActivityRecord record = ActivityRecord.isInStackLocked(token); if (record == null) { - throw new IllegalArgumentException("reportSizeConfigurations: ActivityRecord not " - + "found for: " + token); + return; } record.setSizeConfigurations(horizontalSizeConfiguration, verticalSizeConfigurations, smallestSizeConfigurations); diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index e692ba6905b7..465d089e7904 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -23,20 +23,24 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; +import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import android.annotation.Nullable; +import android.os.Bundle; import android.util.ArrayMap; +import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.Preconditions; import com.android.server.policy.WindowManagerPolicy; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Objects; +import java.util.Set; +import java.util.function.BiFunction; /** * A builder for instantiating a complex {@link DisplayAreaPolicy} @@ -70,22 +74,93 @@ import java.util.Objects; */ class DisplayAreaPolicyBuilder { @Nullable private HierarchyBuilder mRootHierarchyBuilder; + private ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>(); - /** Defines the root hierarchy. */ + /** + * When a window is created, the policy will use this function to select the + * {@link RootDisplayArea} to place that window in. The selected root can be either the one of + * the {@link #mRootHierarchyBuilder} or the one of any of the + * {@link #mDisplayAreaGroupHierarchyBuilders}. + **/ + @Nullable private BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc; + + /** Defines the root hierarchy for the whole logical display. */ DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) { - // TODO(b/157683117): Add method to add sub root and root choosing func. mRootHierarchyBuilder = rootHierarchyBuilder; return this; } + /** + * Defines a DisplayAreaGroup hierarchy. Its root will be added as a child of the root + * hierarchy. + */ + DisplayAreaPolicyBuilder addDisplayAreaGroupHierarchy( + HierarchyBuilder displayAreaGroupHierarchy) { + mDisplayAreaGroupHierarchyBuilders.add(displayAreaGroupHierarchy); + return this; + } + + /** The policy will use this function to find the root to place windows in. */ + DisplayAreaPolicyBuilder setSelectRootForWindowFunc( + BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc) { + mSelectRootForWindowFunc = selectRootForWindowFunc; + return this; + } + + /** Makes sure the setting meets the requirement. */ + private void validate() { + if (mRootHierarchyBuilder == null) { + throw new IllegalStateException("Root must be set for the display area policy."); + } + + boolean containsImeContainer = mRootHierarchyBuilder.mImeContainer != null; + boolean containsDefaultTda = containsDefaultTaskDisplayArea(mRootHierarchyBuilder); + for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) { + HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i); + if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) { + throw new IllegalStateException( + "DisplayAreaGroup must contain at least one TaskDisplayArea."); + } + + containsImeContainer = containsImeContainer || hierarchyBuilder.mImeContainer != null; + containsDefaultTda = containsDefaultTda + || containsDefaultTaskDisplayArea(hierarchyBuilder); + } + + if (!containsImeContainer) { + throw new IllegalStateException("IME container must be set."); + } + + if (!containsDefaultTda) { + throw new IllegalStateException("There must be a default TaskDisplayArea."); + } + } + + /** Checks if the given hierarchy contains the default {@link TaskDisplayArea}. */ + private static boolean containsDefaultTaskDisplayArea(HierarchyBuilder displayAreaHierarchy) { + for (int i = 0; i < displayAreaHierarchy.mTaskDisplayAreas.size(); i++) { + if (displayAreaHierarchy.mTaskDisplayAreas.get(i).mFeatureId + == FEATURE_DEFAULT_TASK_CONTAINER) { + return true; + } + } + return false; + } + Result build(WindowManagerService wmService) { - Objects.requireNonNull(mRootHierarchyBuilder, - "Root must be set for the display area policy."); - Objects.requireNonNull(mRootHierarchyBuilder.mImeContainer, "Ime must not be null"); - Preconditions.checkCollectionNotEmpty(mRootHierarchyBuilder.mTaskDisplayAreas, - "TaskDisplayAreas must not be empty"); - mRootHierarchyBuilder.build(); - return new Result(wmService, mRootHierarchyBuilder.mRoot); + validate(); + + // Attach DA group roots to screen hierarchy before adding windows to group hierarchies. + mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders); + List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>( + mDisplayAreaGroupHierarchyBuilders.size()); + for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) { + HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i); + hierarchyBuilder.build(); + displayAreaGroupRoots.add(hierarchyBuilder.mRoot); + } + return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots, + mSelectRootForWindowFunc); } /** @@ -131,6 +206,14 @@ class DisplayAreaPolicyBuilder { /** Builds the {@link DisplayArea} hierarchy below root. */ private void build() { + build(null /* displayAreaGroupHierarchyBuilders */); + } + + /** + * Builds the {@link DisplayArea} hierarchy below root. And adds the roots of those + * {@link HierarchyBuilder} as children. + */ + private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) { final WindowManagerPolicy policy = mRoot.mWmService.mPolicy; final int maxWindowLayerCount = policy.getMaxWindowLayer(); final DisplayArea.Tokens[] displayAreaForLayer = @@ -215,7 +298,9 @@ class DisplayAreaPolicyBuilder { if (leafType == LEAF_TYPE_TASK_CONTAINERS) { // We use the passed in TaskDisplayAreas for task container type of layer. // Skip creating Tokens even if there is no TDA. - addTaskDisplayAreasToLayer(areaForLayer[layer]); + addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]); + addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer], + displayAreaGroupHierarchyBuilders); leafArea.mSkipTokens = true; } else if (leafType == LEAF_TYPE_IME_CONTAINERS) { // We use the passed in ImeContainer for ime container type of layer. @@ -234,11 +319,11 @@ class DisplayAreaPolicyBuilder { // Notify the root that we have finished attaching all the DisplayAreas. Cache all the // feature related collections there for fast access. - mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas, mTaskDisplayAreas); + mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas); } - /** Adds all {@link TaskDisplayArea} to the specified layer */ - private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea) { + /** Adds all {@link TaskDisplayArea} to the application layer. */ + private void addTaskDisplayAreasToApplicationLayer(PendingArea parentPendingArea) { final int count = mTaskDisplayAreas.size(); for (int i = 0; i < count; i++) { PendingArea leafArea = @@ -249,6 +334,24 @@ class DisplayAreaPolicyBuilder { } } + /** Adds roots of the DisplayAreaGroups to the application layer. */ + private void addDisplayAreaGroupsToApplicationLayer( + DisplayAreaPolicyBuilder.PendingArea parentPendingArea, + @Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) { + if (displayAreaGroupHierarchyBuilders == null) { + return; + } + final int count = displayAreaGroupHierarchyBuilders.size(); + for (int i = 0; i < count; i++) { + DisplayAreaPolicyBuilder.PendingArea + leafArea = new DisplayAreaPolicyBuilder.PendingArea( + null /* feature */, APPLICATION_LAYER, parentPendingArea); + leafArea.mExisting = displayAreaGroupHierarchyBuilders.get(i).mRoot; + leafArea.mMaxLayer = APPLICATION_LAYER; + parentPendingArea.mChildren.add(leafArea); + } + } + private static int typeOfLayer(WindowManagerPolicy policy, int layer) { if (layer == APPLICATION_LAYER) { return LEAF_TYPE_TASK_CONTAINERS; @@ -401,9 +504,20 @@ class DisplayAreaPolicyBuilder { } static class Result extends DisplayAreaPolicy { + final List<RootDisplayArea> mDisplayAreaGroupRoots; + final BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc; - Result(WindowManagerService wmService, RootDisplayArea root) { + Result(WindowManagerService wmService, RootDisplayArea root, + List<RootDisplayArea> displayAreaGroupRoots, + @Nullable BiFunction<WindowToken, Bundle, RootDisplayArea> + selectRootForWindowFunc) { super(wmService, root); + mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots); + mSelectRootForWindowFunc = selectRootForWindowFunc == null + // Always return the highest level root of the logical display when the func is + // not specified. + ? (window, options) -> mRoot + : selectRootForWindowFunc; } @Override @@ -414,27 +528,38 @@ class DisplayAreaPolicyBuilder { @VisibleForTesting DisplayArea.Tokens findAreaForToken(WindowToken token) { - // TODO(b/157683117): Choose root/sub root from OEM provided func. - return mRoot.findAreaForToken(token); + return mSelectRootForWindowFunc.apply(token, token.mOptions).findAreaForToken(token); } @VisibleForTesting List<Feature> getFeatures() { - // TODO(b/157683117): Also get feature from sub root. - return new ArrayList<>(mRoot.mFeatures); + Set<Feature> features = new ArraySet<>(); + features.addAll(mRoot.mFeatures); + for (int i = 0; i < mDisplayAreaGroupRoots.size(); i++) { + features.addAll(mDisplayAreaGroupRoots.get(i).mFeatures); + } + return new ArrayList<>(features); } @Override public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(int featureId) { - // TODO(b/157683117): Also get display areas from sub root. - List<Feature> features = getFeatures(); + List<DisplayArea<? extends WindowContainer>> displayAreas = new ArrayList<>(); + getDisplayAreas(mRoot, featureId, displayAreas); + for (int i = 0; i < mDisplayAreaGroupRoots.size(); i++) { + getDisplayAreas(mDisplayAreaGroupRoots.get(i), featureId, displayAreas); + } + return displayAreas; + } + + private static void getDisplayAreas(RootDisplayArea root, int featureId, + List<DisplayArea<? extends WindowContainer>> displayAreas) { + List<Feature> features = root.mFeatures; for (int i = 0; i < features.size(); i++) { Feature feature = features.get(i); if (feature.mId == featureId) { - return new ArrayList<>(mRoot.mFeatureToDisplayAreas.get(feature)); + displayAreas.addAll(root.mFeatureToDisplayAreas.get(feature)); } } - return new ArrayList<>(); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 7991d2b33a94..0a3b884f79d0 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -71,6 +71,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_TASK_OPEN; import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; +import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; @@ -910,7 +911,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * @param root {@link RootWindowContainer} */ DisplayContent(Display display, RootWindowContainer root) { - super(root.mWindowManager); + super(root.mWindowManager, "DisplayContent", FEATURE_ROOT); if (mWmService.mRoot.getDisplayContent(display.getDisplayId()) != null) { throw new IllegalArgumentException("Display with ID=" + display.getDisplayId() + " already exists=" @@ -3493,12 +3494,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - private void updateImeControlTarget() { + void updateImeControlTarget() { mInputMethodControlTarget = computeImeControlTarget(); mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget); - final WindowState win = mInputMethodControlTarget != null - ? mInputMethodControlTarget.getWindow() : null; + final WindowState win = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget); final IBinder token = win != null ? win.mClient.asBinder() : null; // Note: not allowed to call into IMMS with the WM lock held, hence the post. mWmService.mH.post(() -> @@ -3522,6 +3522,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null) { return mRemoteInsetsControlTarget; } else { + // Now, a special case -- if the last target's window is in the process of exiting, but + // not removed, keep on the last target to avoid IME flicker. + final WindowState cur = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget); + if (cur != null && !cur.mRemoved && cur.isDisplayedLw() && cur.isClosing() + && !cur.isActivityTypeHome()) { + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG_WM, "Not changing control while current window" + + " is closing and not removed"); + } + return cur; + } // Otherwise, we just use the ime target as received from IME. return mInputMethodInputTarget; } diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 8298763c1392..99ee5e121b7a 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -18,13 +18,14 @@ package com.android.server.wm; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME; -import android.graphics.PixelFormat; import android.view.InsetsSource; import android.view.WindowInsets; import com.android.internal.annotations.VisibleForTesting; import com.android.server.protolog.common.ProtoLog; +import java.io.PrintWriter; + /** * Controller for IME inset source on the server. It's called provider as it provides the * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}. @@ -132,8 +133,17 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { || (mImeTargetFromIme != null && dcTarget.getParentWindow() == mImeTargetFromIme && dcTarget.mSubLayer > mImeTargetFromIme.mSubLayer) || mImeTargetFromIme == mDisplayContent.getImeFallback() - // If IME target is transparent but control target matches requesting window. - || (controlTarget == mImeTargetFromIme - && PixelFormat.formatHasAlpha(dcTarget.mAttrs.format)); + || (!mImeTargetFromIme.isClosing() && controlTarget == mImeTargetFromIme); + } + + @Override + public void dump(PrintWriter pw, String prefix) { + super.dump(pw, prefix); + if (mImeTargetFromIme != null) { + pw.print(prefix); + pw.print("showImePostLayout pending for mImeTargetFromIme="); + pw.print(mImeTargetFromIme); + pw.println(); + } } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 8734b5efa45d..0216db471843 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -16,13 +16,14 @@ package com.android.server.wm; -import static android.os.Process.myPid; -import static android.os.Process.myUid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS; @@ -477,12 +478,18 @@ final class InputMonitor { mService.getRecentsAnimationController(); final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord); + final int type = w.mAttrs.type; + final boolean isVisible = w.isVisibleLw(); if (inputChannel == null || inputWindowHandle == null || w.mRemoved || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { if (w.mWinAnimator.hasSurface()) { + // Assign an InputInfo with type to the overlay window which can't receive input + // event. This is used to omit Surfaces from occlusion detection. + populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible); mInputTransaction.setInputWindowInfo( - w.mWinAnimator.mSurfaceController.getClientViewRootSurface(), - mInvalidInputWindow); + w.mWinAnimator.mSurfaceController.getClientViewRootSurface(), + mInvalidInputWindow); + return; } // Skip this window because it cannot possibly receive input. return; @@ -490,9 +497,7 @@ final class InputMonitor { final int flags = w.mAttrs.flags; final int privateFlags = w.mAttrs.privateFlags; - final int type = w.mAttrs.type; final boolean hasFocus = w.isFocused(); - final boolean isVisible = w.isVisibleLw(); if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { if (recentsAnimationController.updateInputConsumerForApp( @@ -555,6 +560,26 @@ final class InputMonitor { } } + // This would reset InputWindowHandle fields to prevent it could be found by input event. + // We need to check if any new field of InputWindowHandle could impact the result. + private static void populateOverlayInputInfo(final InputWindowHandle inputWindowHandle, + final String name, final int type, final boolean isVisible) { + inputWindowHandle.name = name; + inputWindowHandle.layoutParamsType = type; + inputWindowHandle.dispatchingTimeoutNanos = + WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + inputWindowHandle.visible = isVisible; + inputWindowHandle.canReceiveKeys = false; + inputWindowHandle.hasFocus = false; + inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL; + inputWindowHandle.scaleFactor = 1; + inputWindowHandle.layoutParamsFlags = + FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE; + inputWindowHandle.portalToDisplayId = INVALID_DISPLAY; + inputWindowHandle.touchableRegion.setEmpty(); + inputWindowHandle.setTouchableRegionCrop(null); + } + /** * Helper function to generate an InputInfo with type SECURE_SYSTEM_OVERLAY. This input * info will not have an input channel or be touchable, but is used to omit Surfaces @@ -564,16 +589,7 @@ final class InputMonitor { static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t, int displayId, String name) { InputWindowHandle inputWindowHandle = new InputWindowHandle(null, displayId); - inputWindowHandle.name = name; - inputWindowHandle.layoutParamsType = TYPE_SECURE_SYSTEM_OVERLAY; - inputWindowHandle.dispatchingTimeoutNanos = -1; - inputWindowHandle.visible = true; - inputWindowHandle.canReceiveKeys = false; - inputWindowHandle.hasFocus = false; - inputWindowHandle.ownerPid = myPid(); - inputWindowHandle.ownerUid = myUid(); - inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL; - inputWindowHandle.scaleFactor = 1; + populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true); t.setInputWindowInfo(sc, inputWindowHandle); } } diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index c50f296504fc..3ffc26a7a8ad 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -62,4 +62,8 @@ interface InsetsControlTarget { return false; } + /** Returns {@code target.getWindow()}, or null if {@code target} is {@code null}. */ + static WindowState asWindowOrNull(InsetsControlTarget target) { + return target != null ? target.getWindow() : null; + } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 63083faaddb1..9c978fd0c867 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.InsetsState.ITYPE_CAPTION_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_INVALID; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -44,6 +45,7 @@ import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; import android.view.WindowManager; +import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.protolog.common.ProtoLog; import java.io.PrintWriter; @@ -74,7 +76,21 @@ class InsetsStateController { w.notifyInsetsChanged(); } }; - private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { }; + private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { + @Override + public void notifyInsetsControlChanged() { + InsetsSourceControl[] controls = getControlsForDispatch(this); + if (controls == null) { + return; + } + for (InsetsSourceControl control : controls) { + if (control.getType() == ITYPE_IME) { + mDisplayContent.mWmService.mH.post(() -> + InputMethodManagerInternal.get().removeImeSurface()); + } + } + } + }; InsetsStateController(DisplayContent displayContent) { mDisplayContent = displayContent; @@ -171,6 +187,7 @@ class InsetsStateController { state = new InsetsState(state); state.removeSource(ITYPE_STATUS_BAR); state.removeSource(ITYPE_NAVIGATION_BAR); + state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR); } if (aboveIme) { diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java index 9d408540c838..faed7fa99fdd 100644 --- a/services/core/java/com/android/server/wm/RootDisplayArea.java +++ b/services/core/java/com/android/server/wm/RootDisplayArea.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; -import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; @@ -44,19 +43,11 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { /** Mapping from window layer to {@link DisplayArea.Tokens} that holds windows on that layer. */ private DisplayArea.Tokens[] mAreaForLayer; - /** - * List of {@link TaskDisplayArea} that are attached to this {@link DisplayArea} hierarchy. The - * order is the same as their z-order. - * - * TODO(b/157683117): Instead of caching the TDAs, always traverse the hierarchy to get them. - */ - ArrayList<TaskDisplayArea> mTaskDisplayAreas; - /** Whether the hierarchy has been built. */ private boolean mHasBuiltHierarchy; - RootDisplayArea(WindowManagerService wms) { - super(wms, Type.ANY, "RootDisplayArea", FEATURE_ROOT); + RootDisplayArea(WindowManagerService wms, String name, int featureId) { + super(wms, Type.ANY, name, featureId); } /** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */ @@ -73,15 +64,13 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { /** Callback after {@link DisplayArea} hierarchy has been built. */ void onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer, - Map<Feature, List<DisplayArea<? extends WindowContainer>>> featureToDisplayAreas, - ArrayList<TaskDisplayArea> taskDisplayAreas) { + Map<Feature, List<DisplayArea<? extends WindowContainer>>> featureToDisplayAreas) { if (mHasBuiltHierarchy) { throw new IllegalStateException("Root should only build the hierarchy once"); } mHasBuiltHierarchy = true; mFeatures = Collections.unmodifiableList(features); mAreaForLayer = areaForLayer; - mTaskDisplayAreas = taskDisplayAreas; mFeatureToDisplayAreas = featureToDisplayAreas; } } diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index 132d00ac91c1..c27d0cda22ab 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; @@ -24,6 +25,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIG import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.Nullable; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -44,7 +46,13 @@ class WallpaperWindowToken extends WindowToken { WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, DisplayContent dc, boolean ownerCanManageAppTokens) { - super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens); + this(service, token, explicit, dc, ownerCanManageAppTokens, null /* options */); + } + + WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, + DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) { + super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, INVALID_UID, + false /* roundedCornerOverlay */, false /* fromClientToken */, options); dc.mWallpaperController.addWallpaperToken(this); setWindowingMode(WINDOWING_MODE_FULLSCREEN); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index bc4d505bbfca..85972bffd755 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2670,10 +2670,11 @@ public class WindowManagerService extends IWindowManager.Stub } // TODO(window-container): Clean up dead tokens if (type == TYPE_WALLPAPER) { - new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens); + new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens, + options); } else { new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens, - callingUid, false /* roundedCornerOverlay */, fromClientToken); + callingUid, false /* roundedCornerOverlay */, fromClientToken, options); } } } finally { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index bac921816e3f..86bc0a227ac1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2191,6 +2191,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isInputMethodTarget()) { dc.computeImeTarget(true /* updateImeTarget */); } + if (dc.mInputMethodControlTarget == this) { + dc.updateImeControlTarget(); + } final int type = mAttrs.type; if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 86aacf308068..2c1bb3ec51eb 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -40,10 +40,12 @@ import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW; import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER; import android.annotation.CallSuper; +import android.annotation.Nullable; import android.app.IWindowToken; import android.app.servertransaction.FixedRotationAdjustmentsItem; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.Bundle; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; @@ -78,6 +80,13 @@ class WindowToken extends WindowContainer<WindowState> { // The type of window this token is for, as per WindowManager.LayoutParams. final int windowType; + /** + * Options that will be used to determine which {@link RootDisplayArea} this window should be + * attached to. + */ + @Nullable + final Bundle mOptions; + /** {@code true} if this holds the rounded corner overlay */ final boolean mRoundedCornerOverlay; @@ -233,9 +242,17 @@ class WindowToken extends WindowContainer<WindowState> { WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, boolean roundedCornerOverlay, boolean fromClientToken) { + this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, ownerUid, + roundedCornerOverlay, fromClientToken, null /* options */); + } + + WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, + DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, + boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) { super(service); token = _token; windowType = type; + mOptions = options; mPersistOnEmpty = persistOnEmpty; mOwnerCanManageAppTokens = ownerCanManageAppTokens; mOwnerUid = ownerUid; diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java index 04a9cca7fa51..f40d3168cf98 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java @@ -41,8 +41,6 @@ import static org.mockito.MockitoAnnotations.initMocks; import android.app.AppOpsManager; import android.content.Context; -import android.content.Intent; -import android.location.LocationManager; import android.location.util.identity.CallerIdentity; import android.platform.test.annotations.Presubmit; @@ -52,7 +50,6 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import java.util.ArrayList; @@ -183,11 +180,6 @@ public class SystemAppOpsHelperTest { eq(false), eq("myfeature"), nullable(String.class)); assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue(); - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mContext).sendBroadcast(intentCaptor.capture()); - assertThat(intentCaptor.getValue().getAction()).isEqualTo( - LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); - doReturn(MODE_IGNORED).when( mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000), eq("mypackage"), @@ -209,11 +201,6 @@ public class SystemAppOpsHelperTest { mHelper.stopHighPowerLocationMonitoring(identity); verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature"); - - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mContext).sendBroadcast(intentCaptor.capture()); - assertThat(intentCaptor.getValue().getAction()).isEqualTo( - LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); } @Test diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index ac2ec58fae2a..7fc6bbd70000 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -59,6 +59,7 @@ android_test { libs: [ "android.hardware.power-java", "android.hardware.tv.cec-V1.0-java", + "android.hardware.vibrator-java", "android.hidl.manager-V1.0-java", "android.test.mock", "android.test.base", diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 6915220d3fb7..90e1cfcd305a 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -81,6 +81,9 @@ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/> <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"/> + <uses-permission android:name="android.permission.VIBRATE"/> + <uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE"/> + <uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON"/> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java new file mode 100644 index 000000000000..c6922536f61a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.intThat; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.pm.PackageManagerInternal; +import android.hardware.vibrator.IVibrator; +import android.os.Handler; +import android.os.IBinder; +import android.os.IVibratorStateListener; +import android.os.Looper; +import android.os.PowerManager; +import android.os.PowerManagerInternal; +import android.os.PowerSaveState; +import android.os.Process; +import android.os.UserHandle; +import android.os.VibrationAttributes; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; +import android.provider.Settings; +import android.test.mock.MockContentResolver; + +import androidx.test.InstrumentationRegistry; + +import com.android.internal.util.test.FakeSettingsProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.List; + +/** + * Tests for {@link VibratorService}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:VibratorServiceTest + */ +@Presubmit +public class VibratorServiceTest { + + private static final int UID = Process.ROOT_UID; + private static final String PACKAGE_NAME = "package"; + private static final VibrationAttributes ALARM_ATTRS = + new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build(); + private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS = + new VibrationAttributes.Builder().setUsage( + VibrationAttributes.USAGE_TOUCH).build(); + private static final VibrationAttributes NOTIFICATION_ATTRS = + new VibrationAttributes.Builder().setUsage( + VibrationAttributes.USAGE_NOTIFICATION).build(); + private static final VibrationAttributes RINGTONE_ATTRS = + new VibrationAttributes.Builder().setUsage( + VibrationAttributes.USAGE_RINGTONE).build(); + + @Rule public MockitoRule rule = MockitoJUnit.rule(); + + @Mock private PackageManagerInternal mPackageManagerInternalMock; + @Mock private PowerManagerInternal mPowerManagerInternalMock; + @Mock private PowerSaveState mPowerSaveStateMock; + @Mock private Vibrator mVibratorMock; + @Mock private VibratorService.NativeWrapper mNativeWrapperMock; + @Mock private IVibratorStateListener mVibratorStateListenerMock; + @Mock private IBinder mVibratorStateListenerBinderMock; + + private TestLooper mTestLooper; + private ContextWrapper mContextSpy; + + @Before + public void setUp() throws Exception { + mTestLooper = new TestLooper(); + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + + MockContentResolver contentResolver = new MockContentResolver(mContextSpy); + contentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + + when(mContextSpy.getContentResolver()).thenReturn(contentResolver); + when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock); + when(mVibratorMock.getDefaultHapticFeedbackIntensity()) + .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); + when(mVibratorMock.getDefaultNotificationVibrationIntensity()) + .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); + when(mVibratorMock.getDefaultRingVibrationIntensity()) + .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); + when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock); + when(mPackageManagerInternalMock.getSystemUiServiceComponent()) + .thenReturn(new ComponentName("", "")); + when(mPowerManagerInternalMock.getLowPowerState(PowerManager.ServiceType.VIBRATION)) + .thenReturn(mPowerSaveStateMock); + + addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock); + addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock); + FakeSettingsProvider.clearSettingsProvider(); + } + + @After + public void tearDown() throws Exception { + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.removeServiceForTest(PowerManagerInternal.class); + FakeSettingsProvider.clearSettingsProvider(); + } + + private VibratorService createService() { + return new VibratorService(mContextSpy, + new VibratorService.Injector() { + @Override + VibratorService.NativeWrapper getNativeWrapper() { + return mNativeWrapperMock; + } + + @Override + Handler createHandler(Looper looper) { + return new Handler(mTestLooper.getLooper()); + } + + @Override + void addService(String name, IBinder service) { + // ignore + } + }); + } + + @Test + public void createService_initializesNativeService() { + createService(); + verify(mNativeWrapperMock).vibratorInit(); + verify(mNativeWrapperMock).vibratorOff(); + } + + @Test + public void hasVibrator_withVibratorHalPresent_returnsTrue() { + when(mNativeWrapperMock.vibratorExists()).thenReturn(true); + assertTrue(createService().hasVibrator()); + } + + @Test + public void hasVibrator_withNoVibratorHalPresent_returnsFalse() { + when(mNativeWrapperMock.vibratorExists()).thenReturn(false); + assertFalse(createService().hasVibrator()); + } + + @Test + public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() { + when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + assertTrue(createService().hasAmplitudeControl()); + } + + @Test + public void hasAmplitudeControl_withNoAmplitudeControlSupport_returnsFalse() { + when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(false); + assertFalse(createService().hasAmplitudeControl()); + } + + @Test + public void areEffectsSupported_withNullResultFromNative_returnsSupportUnknown() { + when(mNativeWrapperMock.vibratorGetSupportedEffects()).thenReturn(null); + assertArrayEquals(new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN}, + createService().areEffectsSupported(new int[]{VibrationEffect.EFFECT_CLICK})); + } + + @Test + public void areEffectsSupported_withSomeEffectsSupported_returnsSupportYesAndNoForEffects() { + int[] effects = new int[]{VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK}; + + when(mNativeWrapperMock.vibratorGetSupportedEffects()) + .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); + assertArrayEquals( + new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_YES, + Vibrator.VIBRATION_EFFECT_SUPPORT_NO}, + createService().areEffectsSupported(effects)); + } + + @Test + public void arePrimitivesSupported_withoutComposeCapability_returnsAlwaysFalse() { + assertArrayEquals(new boolean[]{false, false}, + createService().arePrimitivesSupported(new int[]{ + VibrationEffect.Composition.PRIMITIVE_CLICK, + VibrationEffect.Composition.PRIMITIVE_TICK + })); + } + + @Test + public void arePrimitivesSupported_withComposeCapability_returnsAlwaysTrue() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + assertArrayEquals(new boolean[]{true, true}, + createService().arePrimitivesSupported(new int[]{ + VibrationEffect.Composition.PRIMITIVE_CLICK, + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE + })); + } + + @Test + public void setAlwaysOnEffect_withCapabilityAndValidEffect_enablesAlwaysOnEffect() { + mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + + assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, + VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS)); + verify(mNativeWrapperMock).vibratorAlwaysOnEnable( + eq(1L), eq((long) VibrationEffect.EFFECT_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG)); + } + + @Test + public void setAlwaysOnEffect_withNonPrebakedEffect_ignoresEffect() { + mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + + assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, + VibrationEffect.createOneShot(100, 255), ALARM_ATTRS)); + verify(mNativeWrapperMock, never()).vibratorAlwaysOnDisable(anyLong()); + verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong()); + } + + @Test + public void setAlwaysOnEffect_withNullEffect_disablesAlwaysOnEffect() { + mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + + assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, null, ALARM_ATTRS)); + verify(mNativeWrapperMock).vibratorAlwaysOnDisable(eq(1L)); + } + + @Test + public void setAlwaysOnEffect_withoutCapability_ignoresEffect() { + assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, + VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS)); + verify(mNativeWrapperMock, never()).vibratorAlwaysOnDisable(anyLong()); + verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong()); + } + + @Test + public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() { + when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + vibrate(service, VibrationEffect.createOneShot(100, 128)); + assertTrue(service.isVibrating()); + + verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).vibratorOn(eq(100L)); + verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128)); + } + + @Test + public void vibrate_withOneShotAndNoAmplitudeControl_turnsVibratorOnAndIgnoresAmplitude() { + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + vibrate(service, VibrationEffect.createOneShot(100, 128)); + assertTrue(service.isVibrating()); + + verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).vibratorOn(eq(100L)); + verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt()); + } + + @Test + public void vibrate_withPrebaked_performsEffect() { + when(mNativeWrapperMock.vibratorGetSupportedEffects()) + .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); + + verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), + any(VibratorService.Vibration.class), eq(false)); + } + + @Test + public void vibrate_withComposed_performsEffect() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10) + .compose(); + vibrate(service, effect); + + ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor = + ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class); + + verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).vibratorPerformComposedEffect( + primitivesCaptor.capture(), any(VibratorService.Vibration.class)); + + // Check all primitive effect fields are passed down to the HAL. + assertEquals(1, primitivesCaptor.getValue().length); + VibrationEffect.Composition.PrimitiveEffect primitive = primitivesCaptor.getValue()[0]; + assertEquals(VibrationEffect.Composition.PRIMITIVE_CLICK, primitive.id); + assertEquals(0.5f, primitive.scale, /* delta= */ 1e-2); + assertEquals(10, primitive.delay); + } + + @Test + public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime() + throws Exception { + when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + VibrationEffect effect = VibrationEffect.createWaveform( + new long[] { 10, 10, 10 }, new int[] { 100, 200, 50 }, -1); + vibrate(service, effect); + + verify(mNativeWrapperMock).vibratorOff(); + + Thread.sleep(5); + verify(mNativeWrapperMock).vibratorOn(eq(30L)); + verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100)); + + Thread.sleep(10); + verify(mNativeWrapperMock).vibratorSetAmplitude(eq(200)); + + Thread.sleep(10); + verify(mNativeWrapperMock).vibratorSetAmplitude(eq(50)); + } + + @Test + public void vibrate_withCallback_finishesVibrationWhenCallbackTriggered() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + doAnswer(invocation -> { + ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); + return null; + }).when(mNativeWrapperMock).vibratorPerformComposedEffect( + any(), any(VibratorService.Vibration.class)); + + // Use vibration with delay so there is time for the callback to be triggered. + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) + .compose(); + vibrate(service, effect); + + // Vibration canceled once before perform and once by native callback. + verify(mNativeWrapperMock, times(2)).vibratorOff(); + verify(mNativeWrapperMock).vibratorPerformComposedEffect( + any(VibrationEffect.Composition.PrimitiveEffect[].class), + any(VibratorService.Vibration.class)); + } + + @Test + public void vibrate_whenBinderDies_cancelsVibration() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + doAnswer(invocation -> { + ((VibratorService.Vibration) invocation.getArgument(1)).binderDied(); + return null; + }).when(mNativeWrapperMock).vibratorPerformComposedEffect( + any(), any(VibratorService.Vibration.class)); + + // Use vibration with delay so there is time for the callback to be triggered. + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) + .compose(); + vibrate(service, effect); + + // Vibration canceled once before perform and once by native binder death. + verify(mNativeWrapperMock, times(2)).vibratorOff(); + verify(mNativeWrapperMock).vibratorPerformComposedEffect( + any(VibrationEffect.Composition.PrimitiveEffect[].class), + any(VibratorService.Vibration.class)); + } + + @Test + public void cancelVibrate_withDeviceVibrating_callsVibratorOff() { + VibratorService service = createService(); + vibrate(service, VibrationEffect.createOneShot(100, 128)); + assertTrue(service.isVibrating()); + Mockito.clearInvocations(mNativeWrapperMock); + + service.cancelVibrate(service); + assertFalse(service.isVibrating()); + verify(mNativeWrapperMock).vibratorOff(); + } + + @Test + public void cancelVibrate_withDeviceNotVibrating_ignoresCall() { + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + service.cancelVibrate(service); + assertFalse(service.isVibrating()); + verify(mNativeWrapperMock, never()).vibratorOff(); + } + + @Test + public void registerVibratorStateListener_callbacksAreTriggered() throws Exception { + VibratorService service = createService(); + + service.registerVibratorStateListener(mVibratorStateListenerMock); + verify(mVibratorStateListenerMock).onVibrating(false); + + vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE)); + verify(mVibratorStateListenerMock).onVibrating(true); + + // Run the scheduled callback to finish one-shot vibration. + mTestLooper.moveTimeForward(10); + mTestLooper.dispatchAll(); + verify(mVibratorStateListenerMock, times(2)).onVibrating(false); + } + + @Test + public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception { + VibratorService service = createService(); + + service.registerVibratorStateListener(mVibratorStateListenerMock); + verify(mVibratorStateListenerMock).onVibrating(false); + + vibrate(service, VibrationEffect.createOneShot(5, VibrationEffect.DEFAULT_AMPLITUDE)); + verify(mVibratorStateListenerMock).onVibrating(true); + + service.unregisterVibratorStateListener(mVibratorStateListenerMock); + Mockito.clearInvocations(mVibratorStateListenerMock); + + vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE)); + verifyNoMoreInteractions(mVibratorStateListenerMock); + } + + @Test + public void scale_withPrebaked_userIntensitySettingAsEffectStrength() { + // Alarm vibration is always VIBRATION_INTENSITY_HIGH. + setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_MEDIUM); + setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, + Vibrator.VIBRATION_INTENSITY_LOW); + setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_OFF); + VibratorService service = createService(); + service.systemReady(); + + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK), + ALARM_ATTRS); + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK), + NOTIFICATION_ATTRS); + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK), + HAPTIC_FEEDBACK_ATTRS); + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK), + RINGTONE_ATTRS); + + verify(mNativeWrapperMock).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any(), anyBoolean()); + verify(mNativeWrapperMock).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_TICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any(), anyBoolean()); + verify(mNativeWrapperMock).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any(), anyBoolean()); + verify(mNativeWrapperMock, never()).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any(), anyBoolean()); + } + + @Test + public void scale_withOneShotAndWaveform_usesScaleLevelOnAmplitude() throws Exception { + when(mVibratorMock.getDefaultNotificationVibrationIntensity()) + .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_HIGH); + setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, + Vibrator.VIBRATION_INTENSITY_LOW); + setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_OFF); + + when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + VibratorService service = createService(); + service.systemReady(); + + vibrate(service, VibrationEffect.createOneShot(20, 100), ALARM_ATTRS); + vibrate(service, VibrationEffect.createOneShot(20, 100), NOTIFICATION_ATTRS); + vibrate(service, VibrationEffect.createOneShot(20, 255), RINGTONE_ATTRS); + vibrate(service, VibrationEffect.createWaveform(new long[] { 10 }, new int[] { 100 }, -1), + HAPTIC_FEEDBACK_ATTRS); + + // Waveform effect runs on a separate thread. + Thread.sleep(5); + + // Alarm vibration is never scaled. + verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100)); + // Notification vibrations will be scaled with SCALE_VERY_HIGH. + verify(mNativeWrapperMock).vibratorSetAmplitude(intThat(amplitude -> amplitude > 150)); + // Haptic feedback vibrations will be scaled with SCALE_LOW. + verify(mNativeWrapperMock).vibratorSetAmplitude( + intThat(amplitude -> amplitude < 100 && amplitude > 50)); + // Ringtone vibration is off. + verify(mNativeWrapperMock, never()).vibratorSetAmplitude(eq(255)); + } + + @Test + public void scale_withComposed_usesScaleLevelOnPrimitiveScaleValues() { + when(mVibratorMock.getDefaultNotificationVibrationIntensity()) + .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_HIGH); + setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, + Vibrator.VIBRATION_INTENSITY_LOW); + setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_OFF); + + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorService service = createService(); + service.systemReady(); + + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) + .compose(); + ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor = + ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class); + + vibrate(service, effect, ALARM_ATTRS); + vibrate(service, effect, NOTIFICATION_ATTRS); + vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS); + vibrate(service, effect, RINGTONE_ATTRS); + + // Ringtone vibration is off, so only the other 3 are propagated to native. + verify(mNativeWrapperMock, times(3)).vibratorPerformComposedEffect( + primitivesCaptor.capture(), any()); + + List<VibrationEffect.Composition.PrimitiveEffect[]> values = + primitivesCaptor.getAllValues(); + + // Alarm vibration is never scaled. + assertEquals(1f, values.get(0)[0].scale, /* delta= */ 1e-2); + assertEquals(0.5f, values.get(0)[1].scale, /* delta= */ 1e-2); + + // Notification vibrations will be scaled with SCALE_VERY_HIGH. + assertEquals(1f, values.get(1)[0].scale, /* delta= */ 1e-2); + assertTrue(0.7 < values.get(1)[1].scale); + + // Haptic feedback vibrations will be scaled with SCALE_LOW. + assertTrue(0.5 < values.get(2)[0].scale); + assertTrue(0.5 > values.get(2)[1].scale); + } + + private void vibrate(VibratorService service, VibrationEffect effect) { + vibrate(service, effect, ALARM_ATTRS); + } + + private void vibrate(VibratorService service, VibrationEffect effect, + VibrationAttributes attributes) { + service.vibrate(UID, PACKAGE_NAME, effect, attributes, "some reason", service); + } + + private void mockVibratorCapabilities(int capabilities) { + when(mNativeWrapperMock.vibratorGetCapabilities()).thenReturn((long) capabilities); + } + + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + + private void setVibrationIntensityUserSetting(String settingName, int value) { + Settings.System.putIntForUser( + mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT); + } +} diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java index 6d9b43a967c4..cd2c9230221c 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -252,6 +252,7 @@ public class RollbackUnitTest { verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(111)); verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(222)); verify(mMockDataHelper, never()).destroyApexDeSnapshots(anyInt()); + verify(mMockDataHelper, never()).destroyApexCeSnapshots(anyInt(), anyInt()); assertThat(rollback.isDeleted()).isTrue(); } @@ -273,6 +274,8 @@ public class RollbackUnitTest { verify(mMockDataHelper, never()) .destroyAppDataSnapshot(anyInt(), pkgRollbackInfoFor(PKG_2), anyInt()); verify(mMockDataHelper).destroyApexDeSnapshots(123); + verify(mMockDataHelper).destroyApexCeSnapshots(111, 123); + verify(mMockDataHelper).destroyApexCeSnapshots(222, 123); assertThat(rollback.isDeleted()).isTrue(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index 290b0e5036bb..a8fc66da9bd0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -16,15 +16,19 @@ package com.android.server.wm; +import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; +import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; @@ -33,13 +37,18 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; import static java.util.stream.Collectors.toList; import android.content.res.Resources; +import android.os.Bundle; +import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; +import com.google.android.collect.Lists; + import org.hamcrest.CustomTypeSafeMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; @@ -74,6 +83,10 @@ public class DisplayAreaPolicyBuilderTest { private DisplayContent mDisplayContent; private TaskDisplayArea mDefaultTaskDisplayArea; private List<TaskDisplayArea> mTaskDisplayAreaList; + private RootDisplayArea mGroupRoot1; + private RootDisplayArea mGroupRoot2; + private TaskDisplayArea mTda1; + private TaskDisplayArea mTda2; @Before public void setup() { @@ -85,6 +98,10 @@ public class DisplayAreaPolicyBuilderTest { FEATURE_DEFAULT_TASK_CONTAINER); mTaskDisplayAreaList = new ArrayList<>(); mTaskDisplayAreaList.add(mDefaultTaskDisplayArea); + mGroupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1", FEATURE_VENDOR_FIRST + 1); + mGroupRoot2 = new SurfacelessDisplayAreaRoot(mWms, "group2", FEATURE_VENDOR_FIRST + 2); + mTda1 = new TaskDisplayArea(mDisplayContent, mWms, "tda1", FEATURE_VENDOR_FIRST + 3); + mTda2 = new TaskDisplayArea(mDisplayContent, mWms, "tda2", FEATURE_VENDOR_FIRST + 4); } @Test @@ -191,6 +208,271 @@ public class DisplayAreaPolicyBuilderTest { } } + @Test + public void testBuilder_singleRoot_validateSettings() { + final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder(); + + // Root must be set. + assertThrows(IllegalStateException.class, () -> builder.build(mWms)); + + // IME must be set. + builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + + assertThrows(IllegalStateException.class, () -> builder.build(mWms)); + + // Default TaskDisplayArea must be set. + builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList( + new TaskDisplayArea(mDisplayContent, mWms, "testTda", + FEATURE_VENDOR_FIRST + 1)))); + + assertThrows(IllegalStateException.class, () -> builder.build(mWms)); + + // No exception + builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + + builder.build(mWms); + } + + @Test + public void testBuilder_displayAreaGroup_validateSettings() { + final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder(); + + // IME must be set to one of the roots. + builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); + builder1.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + + assertThrows(IllegalStateException.class, () -> builder1.build(mWms)); + + // Default TaskDisplayArea must be set. + final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); + builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); + builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList( + new TaskDisplayArea(mDisplayContent, mWms, "testTda", + FEATURE_VENDOR_FIRST + 1)))); + + assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); + + // Each DisplayAreaGroup must have at least one TaskDisplayArea. + final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder(); + builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); + builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2)); + + assertThrows(IllegalStateException.class, () -> builder3.build(mWms)); + + // No exception + final DisplayAreaPolicyBuilder builder4 = new DisplayAreaPolicyBuilder(); + builder4.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); + builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList( + new TaskDisplayArea(mDisplayContent, mWms, "testTda", + FEATURE_VENDOR_FIRST + 1)))); + + builder4.build(mWms); + } + + @Test + public void testBuilder_displayAreaGroup_attachDisplayAreas() { + final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList)) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList(mTda1))) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda2))) + .build(mWms); + + assertThat(mDefaultTaskDisplayArea.isDescendantOf(mRoot)).isTrue(); + assertThat(mGroupRoot1.isDescendantOf(mRoot)).isTrue(); + assertThat(mGroupRoot2.isDescendantOf(mRoot)).isTrue(); + assertThat(mImeContainer.isDescendantOf(mGroupRoot1)).isTrue(); + assertThat(mTda1.isDescendantOf(mGroupRoot1)).isTrue(); + assertThat(mTda2.isDescendantOf(mGroupRoot2)).isTrue(); + assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot1)).isTrue(); + assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot2)).isTrue(); + } + + @Test + public void testBuilder_displayAreaGroup_createFeatureOnGroup() { + final Feature feature1 = new Feature.Builder(mWms.mPolicy, "feature1", + FEATURE_VENDOR_FIRST + 5) + .all() + .except(TYPE_STATUS_BAR) + .build(); + final Feature feature2 = new Feature.Builder(mWms.mPolicy, "feature2", + FEATURE_VENDOR_FIRST + 6) + .upTo(TYPE_STATUS_BAR) + .and(TYPE_NAVIGATION_BAR) + .build(); + final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList)) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList(mTda1)) + .addFeature(feature1)) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda2)) + .addFeature(feature2)) + .build(mWms); + + List<DisplayArea<? extends WindowContainer>> feature1DAs = + policy.getDisplayAreas(feature1.getId()); + List<DisplayArea<? extends WindowContainer>> feature2DAs = + policy.getDisplayAreas(feature2.getId()); + for (DisplayArea<? extends WindowContainer> da : feature1DAs) { + assertThat(da.isDescendantOf(mGroupRoot1)).isTrue(); + } + for (DisplayArea<? extends WindowContainer> da : feature2DAs) { + assertThat(da.isDescendantOf(mGroupRoot2)).isTrue(); + } + } + + @Test + public void testBuilder_addWindow_selectContainerForWindowFunc_defaultFunc() { + final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList)) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList(mTda1))) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda2))) + .build(mWms); + + final WindowToken token = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); + policy.addWindow(token); + + // By default, window are always added to the root. + assertThat(token.isDescendantOf(mRoot)).isTrue(); + assertThat(token.isDescendantOf(mGroupRoot1)).isFalse(); + assertThat(token.isDescendantOf(mGroupRoot2)).isFalse(); + } + + @Test + public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnType() { + final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 = + new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList(mTda1)); + final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 = + new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda2)); + final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList)) + .addDisplayAreaGroupHierarchy(hierarchy1) + .addDisplayAreaGroupHierarchy(hierarchy2) + .setSelectRootForWindowFunc((token, options) -> { + if (token.windowType == TYPE_STATUS_BAR) { + return mGroupRoot1; + } + return mGroupRoot2; + }) + .build(mWms); + + final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); + final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class), + TYPE_WALLPAPER, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); + policy.addWindow(token1); + policy.addWindow(token2); + + assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue(); + assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue(); + } + + @Test + public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnOptions() { + final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 = + new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList); + final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 = + new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList(mTda1)); + final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 = + new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda2)); + final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .setRootHierarchy(hierarchy0) + .addDisplayAreaGroupHierarchy(hierarchy1) + .addDisplayAreaGroupHierarchy(hierarchy2) + .setSelectRootForWindowFunc((token, options) -> { + if (options == null) { + return mRoot; + } + if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot1.mFeatureId) { + return mGroupRoot1; + } + if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot2.mFeatureId) { + return mGroupRoot2; + } + return mRoot; + }) + .build(mWms); + + final Bundle options1 = new Bundle(); + options1.putInt("HIERARCHY_ROOT_ID", mGroupRoot1.mFeatureId); + final Bundle options2 = new Bundle(); + options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId); + final WindowToken token0 = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); + final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, options1); + final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, options2); + + policy.addWindow(token0); + policy.addWindow(token1); + policy.addWindow(token2); + + assertThat(token0.isDescendantOf(mRoot)).isTrue(); + assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue(); + assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue(); + } + private static Resources resourcesWithProvider(String provider) { Resources mock = mock(Resources.class); when(mock.getString( @@ -260,6 +542,10 @@ public class DisplayAreaPolicyBuilderTest { }; } + private boolean isSibling(WindowContainer da1, WindowContainer da2) { + return da1.getParent() != null && da1.getParent() == da2.getParent(); + } + private WindowToken tokenOfType(int type) { WindowToken m = mock(WindowToken.class); when(m.getWindowLayerFromType()).thenReturn( @@ -293,7 +579,11 @@ public class DisplayAreaPolicyBuilderTest { static class SurfacelessDisplayAreaRoot extends RootDisplayArea { SurfacelessDisplayAreaRoot(WindowManagerService wms) { - super(wms); + this(wms, "SurfacelessDisplayAreaRoot", FEATURE_ROOT); + } + + SurfacelessDisplayAreaRoot(WindowManagerService wms, String name, int featureId) { + super(wms, name, featureId); } @Override 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 9bf0a0d381d1..018b3027bdff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -947,6 +947,26 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testComputeImeControlTarget_exitingApp() throws Exception { + final DisplayContent dc = createNewDisplay(); + + WindowState exitingWin = createWindow(null, TYPE_BASE_APPLICATION, "exiting app"); + makeWindowVisible(exitingWin); + exitingWin.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; + exitingWin.mAnimatingExit = true; + + dc.mInputMethodControlTarget = exitingWin; + dc.mInputMethodTarget = dc.mInputMethodInputTarget = + createWindow(null, TYPE_BASE_APPLICATION, "starting app"); + + assertEquals(exitingWin, dc.computeImeControlTarget()); + + exitingWin.removeImmediately(); + + assertEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget()); + } + + @Test public void testComputeImeControlTarget_splitscreen() throws Exception { final DisplayContent dc = createNewDisplay(); dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app"); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index d07f4f117278..1266c50d368e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -149,7 +149,6 @@ public class WindowManagerServiceTests extends WindowTestsBase { TaskDisplayArea secondTda = new TaskDisplayArea(display, mWm, "Tapped TDA", FEATURE_VENDOR_FIRST); display.addChild(secondTda, 1); - display.mTaskDisplayAreas.add(secondTda); // Current focused window ActivityStack focusedStack = createTaskStackOnDisplay( WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 23a097eb0c7c..5264e9aaba27 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -20,16 +20,21 @@ import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import android.content.res.Configuration; +import android.os.Bundle; import android.os.IBinder; import android.platform.test.annotations.Presubmit; @@ -38,6 +43,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.function.BiFunction; + /** * Tests for the {@link WindowToken} class. * @@ -205,4 +212,27 @@ public class WindowTokenTests extends WindowTestsBase { false /* fromClientToken */); assertNotNull(nonClientToken.mSurfaceControl); } + + @Test + public void testWindowAttachedWithOptions() { + BiFunction<WindowToken, Bundle, RootDisplayArea> selectFunc = + ((DisplayAreaPolicyBuilder.Result) mDisplayContent.mDisplayAreaPolicy) + .mSelectRootForWindowFunc; + spyOn(selectFunc); + + final WindowToken token1 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); + + verify(selectFunc).apply(token1, null); + + final Bundle options = new Bundle(); + final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, + false /* fromClientToken */, options /* options */); + + verify(selectFunc).apply(token2, options); + } } diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java index 2d0bd52f84ee..4e4abd0f63b0 100644 --- a/telephony/java/android/telephony/CellLocation.java +++ b/telephony/java/android/telephony/CellLocation.java @@ -16,7 +16,9 @@ package android.telephony; +import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; import android.os.Bundle; import android.os.RemoteException; import android.telephony.cdma.CdmaCellLocation; @@ -31,11 +33,25 @@ import com.android.internal.telephony.PhoneConstants; public abstract class CellLocation { /** - * Request an update of the current location. If the location has changed, - * a broadcast will be sent to everyone registered with {@link - * PhoneStateListener#LISTEN_CELL_LOCATION}. + * This method will not do anything. + * + * Whenever location changes, a callback will automatically be be sent to + * all registrants of {@link PhoneStateListener#LISTEN_CELL_LOCATION}. + * + * <p>This method is a no-op for callers targeting SDK level 31 or greater. + * <p>This method is a no-op for callers that target SDK level 29 or 30 and lack + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + * <p>This method is a no-op for callers that target SDK level 28 or below and lack + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * + * Callers wishing to request a single location update should use + * {@link TelephonyManager#requestCellInfoUpdate}. */ public static void requestLocationUpdate() { + // Since this object doesn't have a context, this is the best we can do. + final Context appContext = ActivityThread.currentApplication(); + if (appContext == null) return; // should never happen + try { ITelephony phone = ITelephony.Stub.asInterface( TelephonyFrameworkInitializer @@ -43,7 +59,7 @@ public abstract class CellLocation { .getTelephonyServiceRegisterer() .get()); if (phone != null) { - phone.updateServiceLocation(); + phone.updateServiceLocationWithPackageName(appContext.getOpPackageName()); } } catch (RemoteException ex) { // ignore it diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 347dcc81ce4e..717a9b155cbf 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -255,28 +255,6 @@ public class SmsMessage { } /** - * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the - * +CMT unsolicited response (PDU mode, of course) - * +CMT: [<alpha>],<length><CR><LF><pdu> - * - * Only public for debugging and for RIL - * - * {@hide} - */ - public static SmsMessage newFromCMT(byte[] pdu) { - // received SMS in 3GPP format - SmsMessageBase wrappedMessage = - com.android.internal.telephony.gsm.SmsMessage.newFromCMT(pdu); - - if (wrappedMessage != null) { - return new SmsMessage(wrappedMessage); - } else { - Rlog.e(LOG_TAG, "newFromCMT(): wrappedMessage is null"); - return null; - } - } - - /** * Creates an SmsMessage from an SMS EF record. * * @param index Index of SMS EF record. diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 3104c20b0063..d96e024d4c60 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2341,58 +2341,6 @@ public class TelephonyManager { } /** - * Enables location update notifications. {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void enableLocationUpdates() { - enableLocationUpdates(getSubId()); - } - - /** - * Enables location update notifications for a subscription. - * {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @param subId for which the location updates are enabled - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void enableLocationUpdates(int subId) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.enableLocationUpdatesForSubscriber(subId); - } catch (RemoteException ex) { - } catch (NullPointerException ex) { - } - } - - /** - * Disables location update notifications. {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void disableLocationUpdates() { - disableLocationUpdates(getSubId()); - } - - /** @hide */ - public void disableLocationUpdates(int subId) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.disableLocationUpdatesForSubscriber(subId); - } catch (RemoteException ex) { - } catch (NullPointerException ex) { - } - } - - /** * Returns the neighboring cell information of the device. * * @return List of NeighboringCellInfo or null if info unavailable. @@ -9290,17 +9238,14 @@ public class TelephonyManager { return RADIO_POWER_UNAVAILABLE; } - /** @hide */ + /** + * This method should not be used due to privacy and stability concerns. + * + * @hide + */ @SystemApi - @SuppressLint("Doclava125") public void updateServiceLocation() { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.updateServiceLocation(); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#updateServiceLocation", e); - } + Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()"); } /** @hide */ diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 4276e713561b..e2de5c82940f 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -222,42 +222,29 @@ interface ITelephony { boolean setRadioPower(boolean turnOn); /** - * Request to update location information in service state + * This method has been removed due to security and stability issues. */ @UnsupportedAppUsage void updateServiceLocation(); /** - * Request to update location information for a subscrition in service state - * @param subId user preferred subId. + * Version of updateServiceLocation that records the caller and validates permissions. */ - void updateServiceLocationForSubscriber(int subId); + void updateServiceLocationWithPackageName(String callingPkg); /** - * Enable location update notifications. + * This method has been removed due to security and stability issues. */ @UnsupportedAppUsage void enableLocationUpdates(); /** - * Enable location update notifications. - * @param subId user preferred subId. - */ - void enableLocationUpdatesForSubscriber(int subId); - - /** - * Disable location update notifications. + * This method has been removed due to security and stability issues. */ @UnsupportedAppUsage void disableLocationUpdates(); /** - * Disable location update notifications. - * @param subId user preferred subId. - */ - void disableLocationUpdatesForSubscriber(int subId); - - /** * Allow mobile data connections. */ @UnsupportedAppUsage diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index d41a6c889afb..d216162cc257 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -171,8 +171,8 @@ public class TelephonyIntents { * if not specified </dd> * </dl> * - * <p class="note"> - * Requires the READ_PHONE_STATE permission. + * <p class="note">This is a sticky broadcast, and therefore requires no permissions to listen + * to. Do not add any additional information to this broadcast. * * <p class="note">This is a protected intent that can only be sent * by the system. diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index ccb14749109d..e4205aaae5b6 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -139,38 +139,6 @@ public class SmsMessage extends SmsMessageBase { } /** - * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the - * +CMT unsolicited response (PDU mode, of course) - * +CMT: [<alpha>],<length><CR><LF><pdu> - * - * Only public for debugging - * - * {@hide} - */ - public static SmsMessage newFromCMT(byte[] pdu) { - try { - SmsMessage msg = new SmsMessage(); - msg.parsePdu(pdu); - return msg; - } catch (RuntimeException ex) { - Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex); - return null; - } - } - - /** @hide */ - public static SmsMessage newFromCDS(byte[] pdu) { - try { - SmsMessage msg = new SmsMessage(); - msg.parsePdu(pdu); - return msg; - } catch (RuntimeException ex) { - Rlog.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex); - return null; - } - } - - /** * Creates an SmsMessage from an SMS EF record. * * @param index Index of SMS EF record. diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index baff952edc72..070dacbcd91f 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -16,7 +16,7 @@ android_test { name: "FlickerTests", - srcs: ["src/**/*.java"], + srcs: ["src/**/*.java", "src/**/*.kt"], manifest: "AndroidManifest.xml", test_config: "AndroidTest.xml", platform_apis: true, diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java deleted file mode 100644 index ed6f33e62031..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java +++ /dev/null @@ -1,174 +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 com.android.server.wm.flicker; - -import static android.view.Surface.rotationToString; - -import static com.android.server.wm.flicker.CommonTransitions.changeAppRotation; -import static com.android.server.wm.flicker.WindowUtils.getAppPosition; -import static com.android.server.wm.flicker.WindowUtils.getNavigationBarPosition; -import static com.android.server.wm.flicker.WindowUtils.getStatusBarPosition; -import static com.android.server.wm.flicker.WmTraceSubject.assertThat; - -import android.graphics.Rect; -import android.util.Log; -import android.view.Surface; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.ArrayList; -import java.util.Collection; - -/** - * Cycle through supported app rotations. - * To run this test: {@code atest FlickerTest:ChangeAppRotationTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class ChangeAppRotationTest extends FlickerTestBase { - private int mBeginRotation; - private int mEndRotation; - - public ChangeAppRotationTest(String beginRotationName, String endRotationName, - int beginRotation, int endRotation) { - this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(), - "com.android.server.wm.flicker.testapp", "SimpleApp"); - this.mBeginRotation = beginRotation; - this.mEndRotation = endRotation; - } - - @Parameters(name = "{0}-{1}") - public static Collection<Object[]> getParams() { - int[] supportedRotations = - {Surface.ROTATION_0, Surface.ROTATION_90}; - Collection<Object[]> params = new ArrayList<>(); - for (int begin : supportedRotations) { - for (int end : supportedRotations) { - if (begin != end) { - params.add(new Object[]{rotationToString(begin), rotationToString(end), begin, - end}); - } - } - } - return params; - } - - @Override - TransitionRunner getTransitionToRun() { - return changeAppRotation(mTestApp, mUiDevice, mBeginRotation, mEndRotation) - .includeJankyRuns().build(); - } - - @FlakyTest(bugId = 140855415) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_navBarWindowIsAlwaysVisible() { - checkResults(result -> assertThat(result) - .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()); - } - - @FlakyTest(bugId = 140855415) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_statusBarWindowIsAlwaysVisible() { - checkResults(result -> assertThat(result) - .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries()); - } - - @Test - public void checkPosition_navBarLayerRotatesAndScales() { - Rect startingPos = getNavigationBarPosition(mBeginRotation); - Rect endingPos = getNavigationBarPosition(mEndRotation); - checkResults(result -> { - LayersTraceSubject.assertThat(result) - .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos) - .inTheBeginning(); - LayersTraceSubject.assertThat(result) - .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos).atTheEnd(); - } - ); - } - - @Test - public void checkPosition_appLayerRotates() { - Rect startingPos = getAppPosition(mBeginRotation); - Rect endingPos = getAppPosition(mEndRotation); - Log.e(TAG, "startingPos=" + startingPos + " endingPos=" + endingPos); - checkResults(result -> { - LayersTraceSubject.assertThat(result) - .hasVisibleRegion(mTestApp.getPackage(), startingPos).inTheBeginning(); - LayersTraceSubject.assertThat(result) - .hasVisibleRegion(mTestApp.getPackage(), endingPos).atTheEnd(); - } - ); - } - - @Test - public void checkPosition_statusBarLayerScales() { - Rect startingPos = getStatusBarPosition(mBeginRotation); - Rect endingPos = getStatusBarPosition(mEndRotation); - checkResults(result -> { - LayersTraceSubject.assertThat(result) - .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos) - .inTheBeginning(); - LayersTraceSubject.assertThat(result) - .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos).atTheEnd(); - } - ); - } - - @Ignore("Flaky. Pending debug") - @Test - public void checkVisibility_screenshotLayerBecomesInvisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(mTestApp.getPackage()) - .then() - .replaceVisibleLayer(mTestApp.getPackage(), SCREENSHOT_LAYER) - .then() - .showsLayer(mTestApp.getPackage()).and().showsLayer(SCREENSHOT_LAYER) - .then() - .replaceVisibleLayer(SCREENSHOT_LAYER, mTestApp.getPackage()) - .forAllEntries()); - } - - @FlakyTest(bugId = 140855415) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_navBarLayerIsAlwaysVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()); - } - - @FlakyTest(bugId = 140855415) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_statusBarLayerIsAlwaysVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java deleted file mode 100644 index 58dcb9948c85..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToApp; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; - -import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -/** - * Test IME window closing back to app window transitions. - * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class CloseImeAutoOpenWindowToAppTest extends CloseImeWindowToAppTest { - - public CloseImeAutoOpenWindowToAppTest(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - - mTestApp = new ImeAppAutoFocusHelper(InstrumentationRegistry.getInstrumentation()); - } - - @Override - TransitionRunner getTransitionToRun() { - return editTextLoseFocusToApp((ImeAppAutoFocusHelper) mTestApp, mUiDevice, mBeginRotation) - .includeJankyRuns().build(); - } - - @FlakyTest(bugId = 141458352) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_imeLayerBecomesInvisible() { - super.checkVisibility_imeLayerBecomesInvisible(); - } - - @FlakyTest(bugId = 141458352) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_imeAppLayerIsAlwaysVisible() { - super.checkVisibility_imeAppLayerIsAlwaysVisible(); - } - - @FlakyTest(bugId = 141458352) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_imeAppWindowIsAlwaysVisible() { - super.checkVisibility_imeAppWindowIsAlwaysVisible(); - } - -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java deleted file mode 100644 index 7f610a695e7e..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToHome; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; - -import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -/** - * Test IME window closing back to app window transitions. - * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class CloseImeAutoOpenWindowToHomeTest extends CloseImeWindowToHomeTest { - - public CloseImeAutoOpenWindowToHomeTest(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - - mTestApp = new ImeAppAutoFocusHelper(InstrumentationRegistry.getInstrumentation()); - } - - @Override - TransitionRunner getTransitionToRun() { - return editTextLoseFocusToHome((ImeAppAutoFocusHelper) mTestApp, mUiDevice, mBeginRotation) - .includeJankyRuns().build(); - } - - @FlakyTest(bugId = 141458352) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_imeWindowBecomesInvisible() { - super.checkVisibility_imeWindowBecomesInvisible(); - } - - @FlakyTest(bugId = 141458352) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_imeLayerBecomesInvisible() { - super.checkVisibility_imeLayerBecomesInvisible(); - } - - @FlakyTest(bugId = 157449248) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_imeAppWindowBecomesInvisible() { - super.checkVisibility_imeAppWindowBecomesInvisible(); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java deleted file mode 100644 index 57ad7e76e540..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java +++ /dev/null @@ -1,79 +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 com.android.server.wm.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToApp; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.LargeTest; - -import com.android.server.wm.flicker.helpers.ImeAppHelper; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -/** - * Test IME window closing back to app window transitions. - * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class CloseImeWindowToAppTest extends NonRotationTestBase { - - static final String IME_WINDOW_TITLE = "InputMethod"; - - public CloseImeWindowToAppTest(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - - mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation()); - } - - @Override - TransitionRunner getTransitionToRun() { - return editTextLoseFocusToApp((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation) - .includeJankyRuns().build(); - } - - @Ignore("Flaky. Pending debug") - @Test - public void checkVisibility_imeLayerBecomesInvisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(IME_WINDOW_TITLE) - .then() - .hidesLayer(IME_WINDOW_TITLE) - .forAllEntries()); - } - - @Test - public void checkVisibility_imeAppLayerIsAlwaysVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(mTestApp.getPackage()) - .forAllEntries()); - } - - @Test - public void checkVisibility_imeAppWindowIsAlwaysVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAppWindowOnTop(mTestApp.getPackage()) - .forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java deleted file mode 100644 index 12d11794c1bd..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java +++ /dev/null @@ -1,98 +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 com.android.server.wm.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToHome; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; - -import com.android.server.wm.flicker.helpers.ImeAppHelper; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -/** - * Test IME window closing to home transitions. - * To run this test: {@code atest FlickerTests:CloseImeWindowToHomeTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class CloseImeWindowToHomeTest extends NonRotationTestBase { - - static final String IME_WINDOW_TITLE = "InputMethod"; - - public CloseImeWindowToHomeTest(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - - mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation()); - } - - @Override - TransitionRunner getTransitionToRun() { - return editTextLoseFocusToHome((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation) - .includeJankyRuns().build(); - } - - @Test - public void checkVisibility_imeWindowBecomesInvisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsImeWindow(IME_WINDOW_TITLE) - .then() - .hidesImeWindow(IME_WINDOW_TITLE) - .forAllEntries()); - } - - @FlakyTest(bugId = 153739621) - @Ignore - @Test - public void checkVisibility_imeLayerBecomesInvisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .skipUntilFirstAssertion() - .showsLayer(IME_WINDOW_TITLE) - .then() - .hidesLayer(IME_WINDOW_TITLE) - .forAllEntries()); - } - - @FlakyTest(bugId = 153739621) - @Ignore - @Test - public void checkVisibility_imeAppLayerBecomesInvisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .skipUntilFirstAssertion() - .showsLayer(mTestApp.getPackage()) - .then() - .hidesLayer(mTestApp.getPackage()) - .forAllEntries()); - } - - @Test - public void checkVisibility_imeAppWindowBecomesInvisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAppWindowOnTop(mTestApp.getPackage()) - .then() - .hidesAppWindowOnTop(mTestApp.getPackage()) - .forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java deleted file mode 100644 index b1854c3294de..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java +++ /dev/null @@ -1,399 +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 com.android.server.wm.flicker; - -import static android.os.SystemClock.sleep; -import static android.view.Surface.rotationToString; - -import static com.android.server.wm.flicker.helpers.AutomationUtils.clearRecents; -import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen; -import static com.android.server.wm.flicker.helpers.AutomationUtils.expandPipWindow; -import static com.android.server.wm.flicker.helpers.AutomationUtils.launchSplitScreen; -import static com.android.server.wm.flicker.helpers.AutomationUtils.stopPackage; - -import android.app.Instrumentation; -import android.content.Context; -import android.content.Intent; -import android.os.RemoteException; -import android.platform.helpers.IAppHelper; -import android.util.Rational; -import android.view.Surface; - -import androidx.annotation.Nullable; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject2; -import androidx.test.uiautomator.Until; - -import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder; -import com.android.server.wm.flicker.helpers.AutomationUtils; -import com.android.server.wm.flicker.helpers.ImeAppHelper; -import com.android.server.wm.flicker.helpers.PipAppHelper; - -/** - * Collection of common transitions which can be used to test different apps or scenarios. - */ -class CommonTransitions { - - public static final int ITERATIONS = 1; - private static final String TAG = "FLICKER"; - private static final long APP_LAUNCH_TIMEOUT = 10000; - - private static void setRotation(UiDevice device, int rotation) { - try { - switch (rotation) { - case Surface.ROTATION_270: - device.setOrientationLeft(); - break; - - case Surface.ROTATION_90: - device.setOrientationRight(); - break; - - case Surface.ROTATION_0: - default: - device.setOrientationNatural(); - } - // Wait for animation to complete - sleep(1000); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param rotation Initial screen rotation - * - * @return test tag with pattern <NAME>__<APP>__<ROTATION> - */ - private static String buildTestTag(String testName, IAppHelper app, int rotation) { - return buildTestTag( - testName, app, /* app2 */ null, rotation, rotation, /* description */ ""); - } - - /** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param beginRotation Initial screen rotation - * @param endRotation End screen rotation (if any, otherwise use same as initial) - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> - */ - private static String buildTestTag(String testName, IAppHelper app, int beginRotation, - int endRotation) { - return buildTestTag( - testName, app, /* app2 */ null, beginRotation, endRotation, /* description */ ""); - } - - /** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param app2 Second app being launched (if any) - * @param beginRotation Initial screen rotation - * @param endRotation End screen rotation (if any, otherwise use same as initial) - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP(S)>__<ROTATION(S)>[__<EXTRA>] - */ - private static String buildTestTag(String testName, IAppHelper app, @Nullable IAppHelper app2, - int beginRotation, int endRotation, String extraInfo) { - StringBuilder testTag = new StringBuilder(); - testTag.append(testName) - .append("__") - .append(app.getLauncherName()); - - if (app2 != null) { - testTag.append("-") - .append(app2.getLauncherName()); - } - - testTag.append("__") - .append(rotationToString(beginRotation)); - - if (endRotation != beginRotation) { - testTag.append("-") - .append(rotationToString(endRotation)); - } - - if (!extraInfo.isEmpty()) { - testTag.append("__") - .append(extraInfo); - } - - return testTag.toString(); - } - - static TransitionBuilder openAppWarm(IAppHelper testApp, UiDevice - device, int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("openAppWarm", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBeforeAll(() -> setRotation(device, beginRotation)) - .runBeforeAll(testApp::open) - .runBefore(device::pressHome) - .runBefore(device::waitForIdle) - .runBefore(() -> setRotation(device, beginRotation)) - .run(testApp::open) - .runAfterAll(testApp::exit) - .runAfterAll(AutomationUtils::setDefaultWait) - .repeat(ITERATIONS); - } - - static TransitionBuilder closeAppWithBackKey(IAppHelper testApp, UiDevice - device, int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("closeAppWithBackKey", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBefore(testApp::open) - .runBefore(device::waitForIdle) - .run(device::pressBack) - .run(device::waitForIdle) - .runAfterAll(testApp::exit) - .runAfterAll(AutomationUtils::setDefaultWait) - .repeat(ITERATIONS); - } - - static TransitionBuilder closeAppWithHomeKey(IAppHelper testApp, UiDevice - device, int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("closeAppWithHomeKey", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBefore(testApp::open) - .runBefore(device::waitForIdle) - .run(device::pressHome) - .run(device::waitForIdle) - .runAfterAll(testApp::exit) - .runAfterAll(AutomationUtils::setDefaultWait) - .repeat(ITERATIONS); - } - - static TransitionBuilder openAppCold(IAppHelper testApp, - UiDevice device, int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("openAppCold", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBefore(device::pressHome) - .runBeforeAll(() -> setRotation(device, beginRotation)) - .runBefore(testApp::exit) - .runBefore(device::waitForIdle) - .run(testApp::open) - .runAfterAll(testApp::exit) - .runAfterAll(() -> setRotation(device, Surface.ROTATION_0)) - .repeat(ITERATIONS); - } - - static TransitionBuilder changeAppRotation(IAppHelper testApp, UiDevice - device, int beginRotation, int endRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("changeAppRotation", testApp, beginRotation, endRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBeforeAll(testApp::open) - .runBefore(() -> setRotation(device, beginRotation)) - .run(() -> setRotation(device, endRotation)) - .runAfterAll(testApp::exit) - .runAfterAll(() -> setRotation(device, Surface.ROTATION_0)) - .repeat(ITERATIONS); - } - - static TransitionBuilder changeAppRotation(Intent intent, String intentId, Context context, - UiDevice device, int beginRotation, int endRotation) { - final String testTag = "changeAppRotation_" + intentId + "_" + - rotationToString(beginRotation) + "_" + rotationToString(endRotation); - return TransitionRunner.newBuilder() - .withTag(testTag) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBeforeAll(() -> { - context.startActivity(intent); - device.wait(Until.hasObject(By.pkg(intent.getComponent() - .getPackageName()).depth(0)), APP_LAUNCH_TIMEOUT); - } - ) - .runBefore(() -> setRotation(device, beginRotation)) - .run(() -> setRotation(device, endRotation)) - .runAfterAll(() -> stopPackage(context, intent.getComponent().getPackageName())) - .runAfterAll(() -> setRotation(device, Surface.ROTATION_0)) - .repeat(ITERATIONS); - } - - static TransitionBuilder appToSplitScreen(IAppHelper testApp, UiDevice device, - int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("appToSplitScreen", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBeforeAll(() -> setRotation(device, beginRotation)) - .runBefore(testApp::open) - .runBefore(device::waitForIdle) - .runBefore(() -> sleep(500)) - .run(() -> launchSplitScreen(device)) - .runAfter(() -> exitSplitScreen(device)) - .runAfterAll(testApp::exit) - .repeat(ITERATIONS); - } - - static TransitionBuilder splitScreenToLauncher(IAppHelper testApp, UiDevice device, - int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("splitScreenToLauncher", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBeforeAll(() -> setRotation(device, beginRotation)) - .runBefore(testApp::open) - .runBefore(device::waitForIdle) - .runBefore(() -> launchSplitScreen(device)) - .run(() -> exitSplitScreen(device)) - .runAfterAll(testApp::exit) - .repeat(ITERATIONS); - } - - static TransitionBuilder editTextSetFocus(ImeAppHelper testApp, UiDevice device, - int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("editTextSetFocus", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBefore(device::pressHome) - .runBefore(() -> setRotation(device, beginRotation)) - .runBefore(testApp::open) - .run(() -> testApp.openIME(device)) - .runAfterAll(testApp::exit) - .repeat(ITERATIONS); - } - - static TransitionBuilder resizeSplitScreen(Instrumentation instr, IAppHelper testAppTop, - ImeAppHelper testAppBottom, UiDevice device, int beginRotation, Rational startRatio, - Rational stopRatio) { - String description = startRatio.toString().replace("/", "-") + "_to_" - + stopRatio.toString().replace("/", "-"); - String testTag = buildTestTag("resizeSplitScreen", testAppTop, testAppBottom, - beginRotation, beginRotation, description); - return TransitionRunner.newBuilder() - .withTag(testTag) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBeforeAll(() -> setRotation(device, beginRotation)) - .runBeforeAll(() -> clearRecents(instr)) - .runBefore(testAppBottom::open) - .runBefore(device::pressHome) - .runBefore(testAppTop::open) - .runBefore(device::waitForIdle) - .runBefore(() -> launchSplitScreen(device)) - .runBefore(() -> { - UiObject2 snapshot = device.findObject( - By.res(device.getLauncherPackageName(), "snapshot")); - snapshot.click(); - }) - .runBefore(() -> testAppBottom.openIME(device)) - .runBefore(device::pressBack) - .runBefore(() -> AutomationUtils.resizeSplitScreen(device, startRatio)) - .run(() -> AutomationUtils.resizeSplitScreen(device, stopRatio)) - .runAfter(() -> exitSplitScreen(device)) - .runAfter(device::pressHome) - .runAfterAll(testAppTop::exit) - .runAfterAll(testAppBottom::exit) - .repeat(ITERATIONS); - } - - static TransitionBuilder editTextLoseFocusToHome(ImeAppHelper testApp, UiDevice device, - int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("editTextLoseFocusToHome", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBefore(device::pressHome) - .runBefore(() -> setRotation(device, beginRotation)) - .runBefore(testApp::open) - .runBefore(() -> testApp.openIME(device)) - .run(device::pressHome) - .run(device::waitForIdle) - .runAfterAll(testApp::exit) - .repeat(ITERATIONS); - } - - static TransitionBuilder editTextLoseFocusToApp(ImeAppHelper testApp, UiDevice device, - int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("editTextLoseFocusToApp", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBefore(device::pressHome) - .runBefore(() -> setRotation(device, beginRotation)) - .runBefore(testApp::open) - .runBefore(() -> testApp.openIME(device)) - .run(device::pressBack) - .run(device::waitForIdle) - .runAfterAll(testApp::exit) - .repeat(ITERATIONS); - } - - static TransitionBuilder enterPipMode(PipAppHelper testApp, UiDevice device, - int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("enterPipMode", testApp, beginRotation)) - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBefore(device::pressHome) - .runBefore(() -> setRotation(device, beginRotation)) - .runBefore(testApp::open) - .run(() -> testApp.clickEnterPipButton(device)) - .runAfter(() -> testApp.closePipWindow(device)) - .runAfterAll(testApp::exit) - .repeat(ITERATIONS); - } - - static TransitionBuilder exitPipModeToHome(PipAppHelper testApp, UiDevice device, - int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("exitPipModeToHome", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .runBefore(device::pressHome) - .runBefore(() -> setRotation(device, beginRotation)) - .runBefore(testApp::open) - .run(() -> testApp.clickEnterPipButton(device)) - .run(() -> testApp.closePipWindow(device)) - .run(device::waitForIdle) - .run(testApp::exit) - .repeat(ITERATIONS); - } - - static TransitionBuilder exitPipModeToApp(PipAppHelper testApp, UiDevice device, - int beginRotation) { - return TransitionRunner.newBuilder() - .withTag(buildTestTag("exitPipModeToApp", testApp, beginRotation)) - .recordAllRuns() - .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) - .run(device::pressHome) - .run(() -> setRotation(device, beginRotation)) - .run(testApp::open) - .run(() -> testApp.clickEnterPipButton(device)) - .run(() -> expandPipWindow(device)) - .run(device::waitForIdle) - .run(testApp::exit) - .repeat(ITERATIONS); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.kt new file mode 100644 index 000000000000..b69e6a9736a3 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.kt @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker + +import android.app.Instrumentation +import android.content.Context +import android.content.Intent +import android.os.RemoteException +import android.os.SystemClock +import android.platform.helpers.IAppHelper +import android.util.Rational +import android.view.Surface +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.helpers.AutomationUtils +import com.android.server.wm.flicker.helpers.ImeAppHelper +import com.android.server.wm.flicker.helpers.PipAppHelper + +/** + * Collection of common transitions which can be used to test different apps or scenarios. + */ +internal object CommonTransitions { + private const val ITERATIONS = 1 + private const val APP_LAUNCH_TIMEOUT: Long = 10000 + private fun setRotation(device: UiDevice, rotation: Int) { + try { + when (rotation) { + Surface.ROTATION_270 -> device.setOrientationLeft() + Surface.ROTATION_90 -> device.setOrientationRight() + Surface.ROTATION_0 -> device.setOrientationNatural() + else -> device.setOrientationNatural() + } + // Wait for animation to complete + SystemClock.sleep(1000) + } catch (e: RemoteException) { + throw RuntimeException(e) + } + } + + /** + * Build a test tag for the test + * @param testName Name of the transition(s) being tested + * @param app App being launcher + * @param rotation Initial screen rotation + * + * @return test tag with pattern <NAME>__<APP>__<ROTATION> + </ROTATION></APP></NAME> */ + private fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String { + return buildTestTag( + testName, app, rotation, rotation, app2 = null, extraInfo = "") + } + + /** + * Build a test tag for the test + * @param testName Name of the transition(s) being tested + * @param app App being launcher + * @param beginRotation Initial screen rotation + * @param endRotation End screen rotation (if any, otherwise use same as initial) + * + * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> + </END_ROTATION></BEGIN_ROTATION></APP></NAME> */ + private fun buildTestTag( + testName: String, + app: IAppHelper, + beginRotation: Int, + endRotation: Int + ): String { + return buildTestTag( + testName, app, beginRotation, endRotation, app2 = null, extraInfo = "") + } + + /** + * Build a test tag for the test + * @param testName Name of the transition(s) being tested + * @param app App being launcher + * @param app2 Second app being launched (if any) + * @param beginRotation Initial screen rotation + * @param endRotation End screen rotation (if any, otherwise use same as initial) + * @param extraInfo Additional information to append to the tag + * + * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>] + </EXTRA></NAME> */ + private fun buildTestTag( + testName: String, + app: IAppHelper, + beginRotation: Int, + endRotation: Int, + app2: IAppHelper?, + extraInfo: String + ): String { + val testTag = StringBuilder() + testTag.append(testName) + .append("__") + .append(app.launcherName) + if (app2 != null) { + testTag.append("-") + .append(app2.launcherName) + } + testTag.append("__") + .append(Surface.rotationToString(beginRotation)) + if (endRotation != beginRotation) { + testTag.append("-") + .append(Surface.rotationToString(endRotation)) + } + if (extraInfo.isNotEmpty()) { + testTag.append("__") + .append(extraInfo) + } + return testTag.toString() + } + + fun openAppWarm( + testApp: IAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("openAppWarm", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBeforeAll { setRotation(device, beginRotation) } + .runBeforeAll { testApp.open() } + .runBefore { device.pressHome() } + .runBefore { device.waitForIdle() } + .runBefore { setRotation(device, beginRotation) } + .run { testApp.open() } + .runAfterAll { testApp.exit() } + .runAfterAll { AutomationUtils.setDefaultWait() } + .repeat(ITERATIONS) + } + + fun closeAppWithBackKey( + testApp: IAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("closeAppWithBackKey", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBefore { testApp.open() } + .runBefore { device.waitForIdle() } + .run { device.pressBack() } + .run { device.waitForIdle() } + .runAfterAll { testApp.exit() } + .runAfterAll { AutomationUtils.setDefaultWait() } + .repeat(ITERATIONS) + } + + fun closeAppWithHomeKey( + testApp: IAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("closeAppWithHomeKey", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBefore { testApp.open() } + .runBefore { device.waitForIdle() } + .run { device.pressHome() } + .run { device.waitForIdle() } + .runAfterAll { testApp.exit() } + .runAfterAll { AutomationUtils.setDefaultWait() } + .repeat(ITERATIONS) + } + + fun openAppCold( + testApp: IAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("openAppCold", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBefore { device.pressHome() } + .runBeforeAll { setRotation(device, beginRotation) } + .runBefore { testApp.exit() } + .runBefore { device.waitForIdle() } + .run { testApp.open() } + .runAfterAll { testApp.exit() } + .runAfterAll { setRotation(device, Surface.ROTATION_0) } + .repeat(ITERATIONS) + } + + fun changeAppRotation( + testApp: IAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int, + endRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("changeAppRotation", testApp, beginRotation, endRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBeforeAll { testApp.open() } + .runBefore { setRotation(device, beginRotation) } + .run { setRotation(device, endRotation) } + .runAfterAll { testApp.exit() } + .runAfterAll { setRotation(device, Surface.ROTATION_0) } + .repeat(ITERATIONS) + } + + fun changeAppRotation( + intent: Intent, + intentId: String, + context: Context, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int, + endRotation: Int + ): TransitionRunner.TransitionBuilder { + val testTag = "changeAppRotation_" + intentId + "_" + + Surface.rotationToString(beginRotation) + "_" + + Surface.rotationToString(endRotation) + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(testTag) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBeforeAll { + context.startActivity(intent) + device.wait(Until.hasObject(By.pkg(intent.component?.packageName) + .depth(0)), APP_LAUNCH_TIMEOUT) + } + .runBefore { setRotation(device, beginRotation) } + .run { setRotation(device, endRotation) } + .runAfterAll { AutomationUtils.stopPackage(context, intent.component?.packageName) } + .runAfterAll { setRotation(device, Surface.ROTATION_0) } + .repeat(ITERATIONS) + } + + fun appToSplitScreen( + testApp: IAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("appToSplitScreen", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBeforeAll { setRotation(device, beginRotation) } + .runBefore { testApp.open() } + .runBefore { device.waitForIdle() } + .runBefore { SystemClock.sleep(500) } + .run { AutomationUtils.launchSplitScreen(device) } + .runAfter { AutomationUtils.exitSplitScreen(device) } + .runAfterAll { testApp.exit() } + .repeat(ITERATIONS) + } + + fun splitScreenToLauncher( + testApp: IAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("splitScreenToLauncher", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBefore { testApp.open() } + .runBefore { device.waitForIdle() } + .runBefore { AutomationUtils.launchSplitScreen(device) } + .run { AutomationUtils.exitSplitScreen(device) } + .runAfterAll { testApp.exit() } + .repeat(ITERATIONS) + } + + fun editTextSetFocus( + testApp: ImeAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("editTextSetFocus", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBefore { device.pressHome() } + .runBefore { setRotation(device, beginRotation) } + .runBefore { testApp.open() } + .run { testApp.openIME(device) } + .runAfterAll { testApp.exit() } + .repeat(ITERATIONS) + } + + fun resizeSplitScreen( + testAppTop: IAppHelper, + testAppBottom: ImeAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int, + startRatio: Rational, + stopRatio: Rational + ): TransitionRunner.TransitionBuilder { + val description = (startRatio.toString().replace("/", "-") + "_to_" + + stopRatio.toString().replace("/", "-")) + val testTag = buildTestTag("resizeSplitScreen", testAppTop, beginRotation, + beginRotation, testAppBottom, description) + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(testTag) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBeforeAll { setRotation(device, beginRotation) } + .runBeforeAll { AutomationUtils.clearRecents(instrumentation) } + .runBefore { testAppBottom.open() } + .runBefore { device.pressHome() } + .runBefore { testAppTop.open() } + .runBefore { device.waitForIdle() } + .runBefore { AutomationUtils.launchSplitScreen(device) } + .runBefore { + val snapshot = device.findObject( + By.res(device.launcherPackageName, "snapshot")) + snapshot.click() + } + .runBefore { testAppBottom.openIME(device) } + .runBefore { device.pressBack() } + .runBefore { AutomationUtils.resizeSplitScreen(device, startRatio) } + .run { AutomationUtils.resizeSplitScreen(device, stopRatio) } + .runAfter { AutomationUtils.exitSplitScreen(device) } + .runAfter { device.pressHome() } + .runAfterAll { testAppTop.exit() } + .runAfterAll { testAppBottom.exit() } + .repeat(ITERATIONS) + } + + fun editTextLoseFocusToHome( + testApp: ImeAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("editTextLoseFocusToHome", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBefore { device.pressHome() } + .runBefore { setRotation(device, beginRotation) } + .runBefore { testApp.open() } + .runBefore { testApp.openIME(device) } + .run { device.pressHome() } + .run { device.waitForIdle() } + .runAfterAll { testApp.exit() } + .repeat(ITERATIONS) + } + + fun editTextLoseFocusToApp( + testApp: ImeAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("editTextLoseFocusToApp", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBefore { device.pressHome() } + .runBefore { setRotation(device, beginRotation) } + .runBefore { testApp.open() } + .runBefore { testApp.openIME(device) } + .run { device.pressBack() } + .run { device.waitForIdle() } + .runAfterAll { testApp.exit() } + .repeat(ITERATIONS) + } + + fun enterPipMode( + testApp: PipAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("enterPipMode", testApp, beginRotation)) + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBefore { device.pressHome() } + .runBefore { setRotation(device, beginRotation) } + .runBefore { testApp.open() } + .run { testApp.clickEnterPipButton(device) } + .runAfter { testApp.closePipWindow(device) } + .runAfterAll { testApp.exit() } + .repeat(ITERATIONS) + } + + fun exitPipModeToHome( + testApp: PipAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("exitPipModeToHome", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .runBefore { device.pressHome() } + .runBefore { setRotation(device, beginRotation) } + .runBefore { testApp.open() } + .run { testApp.clickEnterPipButton(device) } + .run { testApp.closePipWindow(device) } + .run { device.waitForIdle() } + .run { testApp.exit() } + .repeat(ITERATIONS) + } + + fun exitPipModeToApp( + testApp: PipAppHelper, + instrumentation: Instrumentation, + device: UiDevice, + beginRotation: Int + ): TransitionRunner.TransitionBuilder { + return TransitionRunner.TransitionBuilder(instrumentation) + .withTag(buildTestTag("exitPipModeToApp", testApp, beginRotation)) + .recordAllRuns() + .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() } + .run { device.pressHome() } + .run { setRotation(device, beginRotation) } + .run { testApp.open() } + .run { testApp.clickEnterPipButton(device) } + .run { AutomationUtils.expandPipWindow(device) } + .run { device.waitForIdle() } + .run { testApp.exit() } + .repeat(ITERATIONS) + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java deleted file mode 100644 index dec5680e17f5..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java +++ /dev/null @@ -1,193 +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 com.android.server.wm.flicker; - -import android.app.Instrumentation; -import android.platform.helpers.IAppHelper; -import android.util.Rational; -import android.view.Surface; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; -import androidx.test.uiautomator.UiDevice; - -import com.android.server.wm.flicker.helpers.ImeAppHelper; -import com.android.server.wm.flicker.helpers.PipAppHelper; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; - -/** - * Tests to help debug individual transitions, capture video recordings and create test cases. - */ -@LargeTest -@Ignore("Used for debugging transitions used in FlickerTests.") -@RunWith(AndroidJUnit4.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class DebugTest { - private IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(), - "com.android.server.wm.flicker.testapp", "SimpleApp"); - private UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - - /** - * atest FlickerTests:DebugTest#openAppCold - */ - @Test - public void openAppCold() { - CommonTransitions.openAppCold(testApp, uiDevice, Surface.ROTATION_0) - .recordAllRuns().build().run(); - } - - /** - * atest FlickerTests:DebugTest#openAppWarm - */ - @Test - public void openAppWarm() { - CommonTransitions.openAppWarm(testApp, uiDevice, Surface.ROTATION_0) - .recordAllRuns().build().run(); - } - - /** - * atest FlickerTests:DebugTest#changeOrientationFromNaturalToLeft - */ - @Test - public void changeOrientationFromNaturalToLeft() { - CommonTransitions.changeAppRotation(testApp, uiDevice, Surface.ROTATION_0, - Surface.ROTATION_270).recordAllRuns().build().run(); - } - - /** - * atest FlickerTests:DebugTest#closeAppWithBackKey - */ - @Test - public void closeAppWithBackKey() { - CommonTransitions.closeAppWithBackKey(testApp, uiDevice, Surface.ROTATION_0) - .recordAllRuns().build().run(); - } - - /** - * atest FlickerTests:DebugTest#closeAppWithHomeKey - */ - @Test - public void closeAppWithHomeKey() { - CommonTransitions.closeAppWithHomeKey(testApp, uiDevice, Surface.ROTATION_0) - .recordAllRuns().build().run(); - } - - /** - * atest FlickerTests:DebugTest#openAppToSplitScreen - */ - @Test - public void openAppToSplitScreen() { - CommonTransitions.appToSplitScreen(testApp, uiDevice, - Surface.ROTATION_0).includeJankyRuns().recordAllRuns() - .build().run(); - } - - /** - * atest FlickerTests:DebugTest#splitScreenToLauncher - */ - @Test - public void splitScreenToLauncher() { - CommonTransitions.splitScreenToLauncher(testApp, uiDevice, Surface.ROTATION_0) - .includeJankyRuns().recordAllRuns().build().run(); - } - - /** - * atest FlickerTests:DebugTest#resizeSplitScreen - */ - @Test - public void resizeSplitScreen() { - Instrumentation instr = InstrumentationRegistry.getInstrumentation(); - ImeAppHelper bottomApp = new ImeAppHelper(instr); - CommonTransitions.resizeSplitScreen(instr, testApp, bottomApp, uiDevice, Surface.ROTATION_0, - new Rational(1, 3), new Rational(2, 3)) - .includeJankyRuns().build().run(); - } - - // IME tests - - /** - * atest FlickerTests:DebugTest#editTextSetFocus - */ - @Test - public void editTextSetFocus() { - ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation()); - CommonTransitions.editTextSetFocus(testApp, uiDevice, Surface.ROTATION_0) - .includeJankyRuns() - .build().run(); - } - - /** - * atest FlickerTests:DebugTest#editTextLoseFocusToHome - */ - @Test - public void editTextLoseFocusToHome() { - ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation()); - CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0) - .includeJankyRuns() - .build().run(); - } - - /** - * atest FlickerTests:DebugTest#editTextLoseFocusToApp - */ - @Test - public void editTextLoseFocusToApp() { - ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation()); - CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0) - .includeJankyRuns() - .build().run(); - } - - // PIP tests - - /** - * atest FlickerTests:DebugTest#enterPipMode - */ - @Test - public void enterPipMode() { - PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation()); - CommonTransitions.enterPipMode(testApp, uiDevice, Surface.ROTATION_0).includeJankyRuns() - .build().run(); - } - - /** - * atest FlickerTests:DebugTest#exitPipModeToHome - */ - @Test - public void exitPipModeToHome() { - PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation()); - CommonTransitions.exitPipModeToHome(testApp, uiDevice, Surface.ROTATION_0) - .includeJankyRuns() - .build().run(); - } - - /** - * atest FlickerTests:DebugTest#exitPipModeToApp - */ - @Test - public void exitPipModeToApp() { - PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation()); - CommonTransitions.exitPipModeToApp(testApp, uiDevice, Surface.ROTATION_0).includeJankyRuns() - .build().run(); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt new file mode 100644 index 000000000000..f871ea59ea71 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker + +import android.platform.helpers.IAppHelper +import android.util.Rational +import android.view.Surface +import androidx.test.InstrumentationRegistry +import androidx.test.filters.LargeTest +import androidx.test.runner.AndroidJUnit4 +import androidx.test.uiautomator.UiDevice +import com.android.server.wm.flicker.helpers.ImeAppHelper +import com.android.server.wm.flicker.helpers.PipAppHelper +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters + +/** + * Tests to help debug individual transitions, capture video recordings and create test cases. + */ +@LargeTest +@Ignore("Used for debugging transitions used in FlickerTests.") +@RunWith(AndroidJUnit4::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class DebugTest { + private val instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp: IAppHelper = StandardAppHelper(instrumentation, + "com.android.server.wm.flicker.testapp", "SimpleApp") + private val uiDevice = UiDevice.getInstance(instrumentation) + + /** + * atest FlickerTests:DebugTest#openAppCold + */ + @Test + fun openAppCold() { + CommonTransitions.openAppCold(testApp, instrumentation, uiDevice, Surface.ROTATION_0) + .recordAllRuns().build().run() + } + + /** + * atest FlickerTests:DebugTest#openAppWarm + */ + @Test + fun openAppWarm() { + CommonTransitions.openAppWarm(testApp, instrumentation, uiDevice, Surface.ROTATION_0) + .recordAllRuns().build().run() + } + + /** + * atest FlickerTests:DebugTest#changeOrientationFromNaturalToLeft + */ + @Test + fun changeOrientationFromNaturalToLeft() { + CommonTransitions.changeAppRotation(testApp, instrumentation, uiDevice, Surface.ROTATION_0, + Surface.ROTATION_270).recordAllRuns().build().run() + } + + /** + * atest FlickerTests:DebugTest#closeAppWithBackKey + */ + @Test + fun closeAppWithBackKey() { + CommonTransitions.closeAppWithBackKey(testApp, instrumentation, uiDevice, + Surface.ROTATION_0).recordAllRuns().build().run() + } + + /** + * atest FlickerTests:DebugTest#closeAppWithHomeKey + */ + @Test + fun closeAppWithHomeKey() { + CommonTransitions.closeAppWithHomeKey(testApp, instrumentation, uiDevice, + Surface.ROTATION_0).recordAllRuns().build().run() + } + + /** + * atest FlickerTests:DebugTest#openAppToSplitScreen + */ + @Test + fun openAppToSplitScreen() { + CommonTransitions.appToSplitScreen(testApp, instrumentation, uiDevice, + Surface.ROTATION_0).includeJankyRuns().recordAllRuns() + .build().run() + } + + /** + * atest FlickerTests:DebugTest#splitScreenToLauncher + */ + @Test + fun splitScreenToLauncher() { + CommonTransitions.splitScreenToLauncher(testApp, instrumentation, uiDevice, + Surface.ROTATION_0).includeJankyRuns().recordAllRuns().build().run() + } + + /** + * atest FlickerTests:DebugTest#resizeSplitScreen + */ + @Test + fun resizeSplitScreen() { + val bottomApp = ImeAppHelper(instrumentation) + CommonTransitions.resizeSplitScreen( + testApp, + bottomApp, + instrumentation, + uiDevice, + Surface.ROTATION_0, + Rational(1, 3), Rational(2, 3) + ).includeJankyRuns().build().run() + } + // IME tests + /** + * atest FlickerTests:DebugTest#editTextSetFocus + */ + @Test + fun editTextSetFocus() { + val testApp = ImeAppHelper(instrumentation) + CommonTransitions.editTextSetFocus(testApp, instrumentation, uiDevice, Surface.ROTATION_0) + .includeJankyRuns() + .build().run() + } + + /** + * atest FlickerTests:DebugTest#editTextLoseFocusToHome + */ + @Test + fun editTextLoseFocusToHome() { + val testApp = ImeAppHelper(instrumentation) + CommonTransitions.editTextLoseFocusToHome(testApp, instrumentation, uiDevice, + Surface.ROTATION_0).includeJankyRuns().build().run() + } + + /** + * atest FlickerTests:DebugTest#editTextLoseFocusToApp + */ + @Test + fun editTextLoseFocusToApp() { + val testApp = ImeAppHelper(instrumentation) + CommonTransitions.editTextLoseFocusToHome(testApp, instrumentation, uiDevice, + Surface.ROTATION_0).includeJankyRuns().build().run() + } + // PIP tests + /** + * atest FlickerTests:DebugTest#enterPipMode + */ + @Test + fun enterPipMode() { + val testApp = PipAppHelper(instrumentation) + CommonTransitions.enterPipMode(testApp, instrumentation, uiDevice, Surface.ROTATION_0) + .includeJankyRuns().build().run() + } + + /** + * atest FlickerTests:DebugTest#exitPipModeToHome + */ + @Test + fun exitPipModeToHome() { + val testApp = PipAppHelper(instrumentation) + CommonTransitions.exitPipModeToHome(testApp, instrumentation, uiDevice, Surface.ROTATION_0) + .includeJankyRuns() + .build().run() + } + + /** + * atest FlickerTests:DebugTest#exitPipModeToApp + */ + @Test + fun exitPipModeToApp() { + val testApp = PipAppHelper(instrumentation) + CommonTransitions.exitPipModeToApp(testApp, instrumentation, uiDevice, Surface.ROTATION_0) + .includeJankyRuns().build().run() + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java deleted file mode 100644 index 6bbf684d567f..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java +++ /dev/null @@ -1,146 +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 com.android.server.wm.flicker; - -import static com.android.server.wm.flicker.helpers.AutomationUtils.setDefaultWait; - -import static com.google.common.truth.Truth.assertWithMessage; - -import android.platform.helpers.IAppHelper; -import android.util.Log; - -import androidx.test.InstrumentationRegistry; -import androidx.test.uiautomator.UiDevice; - -import com.android.server.wm.flicker.TransitionRunner.TransitionResult; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; - -import java.util.HashMap; -import java.util.List; -import java.util.function.Consumer; - -/** - * Base class of all Flicker test that performs common functions for all flicker tests: - * <p> - * - Caches transitions so that a transition is run once and the transition results are used by - * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods - * multiple times. - * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed. - * - Fails tests if results are not available for any test due to jank. - */ -public abstract class FlickerTestBase { - public static final String TAG = "FLICKER"; - static final String SCREENSHOT_LAYER = "RotationLayer"; - static final String NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"; - static final String STATUS_BAR_WINDOW_TITLE = "StatusBar"; - static final String DOCKED_STACK_DIVIDER = "DockedStackDivider"; - private static HashMap<String, List<TransitionResult>> transitionResults = - new HashMap<>(); - IAppHelper mTestApp; - UiDevice mUiDevice; - private List<TransitionResult> mResults; - private TransitionResult mLastResult = null; - - @Before - public void setUp() { - mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - } - - /** - * Teardown any system settings and clean up test artifacts from the file system. - * - * Note: test artifacts for failed tests will remain on the device. - */ - @AfterClass - public static void teardown() { - setDefaultWait(); - transitionResults.values().stream() - .flatMap(List::stream) - .forEach(result -> { - if (result.canDelete()) { - result.delete(); - } else { - if (result.layersTraceExists()) { - Log.e(TAG, "Layers trace saved to " + result.getLayersTracePath()); - } - if (result.windowManagerTraceExists()) { - Log.e(TAG, "WindowManager trace saved to " + result - .getWindowManagerTracePath - ()); - } - if (result.screenCaptureVideoExists()) { - Log.e(TAG, "Screen capture video saved to " + result - .screenCaptureVideoPath().toString()); - } - } - }); - } - - /** - * Runs a transition, returns a cached result if the transition has run before. - */ - void run(TransitionRunner transition) { - if (transitionResults.containsKey(transition.getTestTag())) { - mResults = transitionResults.get(transition.getTestTag()); - return; - } - mResults = transition.run().getResults(); - /* Fail if we don't have any results due to jank */ - assertWithMessage("No results to test because all transition runs were invalid because " - + "of Jank").that(mResults).isNotEmpty(); - transitionResults.put(transition.getTestTag(), mResults); - } - - /** - * Runs a transition, returns a cached result if the transition has run before. - */ - @Before - public void runTransition() { - run(getTransitionToRun()); - } - - /** - * Gets the transition that will be executed - */ - abstract TransitionRunner getTransitionToRun(); - - /** - * Goes through a list of transition results and checks assertions on each result. - */ - void checkResults(Consumer<TransitionResult> assertion) { - - for (TransitionResult result : mResults) { - mLastResult = result; - assertion.accept(result); - } - mLastResult = null; - } - - /** - * Kludge to mark a file for saving. If {@code checkResults} fails, the last result is not - * cleared. This indicates the assertion failed for the result, so mark it for saving. - */ - @After - public void markArtifactsForSaving() { - if (mLastResult != null) { - mLastResult.flagForSaving(); - } - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt new file mode 100644 index 000000000000..d7586d0db915 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker + +import android.platform.helpers.IAppHelper +import android.util.Log +import androidx.test.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.server.wm.flicker.helpers.AutomationUtils +import com.google.common.truth.Truth +import org.junit.After +import org.junit.AfterClass +import org.junit.Before + +/** + * Base class of all Flicker test that performs common functions for all flicker tests: + * + * + * - Caches transitions so that a transition is run once and the transition results are used by + * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods + * multiple times. + * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed. + * - Fails tests if results are not available for any test due to jank. + */ +abstract class FlickerTestBase { + lateinit var testApp: IAppHelper + open val instrumentation by lazy { + InstrumentationRegistry.getInstrumentation() + } + val uiDevice by lazy { + UiDevice.getInstance(instrumentation) + } + lateinit var tesults: List<TransitionResult> + private var lastResult: TransitionResult? = null + + /** + * Runs a transition, returns a cached result if the transition has run before. + */ + fun run(transition: TransitionRunner) { + if (transitionResults.containsKey(transition.testTag)) { + tesults = transitionResults[transition.testTag] + ?: throw IllegalStateException("Results do not contain test tag " + + transition.testTag) + return + } + tesults = transition.run().results + /* Fail if we don't have any results due to jank */ + Truth.assertWithMessage("No results to test because all transition runs were invalid " + + "because of Jank").that(tesults).isNotEmpty() + transitionResults[transition.testTag] = tesults + } + + /** + * Runs a transition, returns a cached result if the transition has run before. + */ + @Before + fun runTransition() { + run(transitionToRun) + } + + /** + * Gets the transition that will be executed + */ + abstract val transitionToRun: TransitionRunner + + /** + * Goes through a list of transition results and checks assertions on each result. + */ + fun checkResults(assertion: (TransitionResult) -> Unit) { + for (result in tesults) { + lastResult = result + assertion(result) + } + lastResult = null + } + + /** + * Kludge to mark a file for saving. If `checkResults` fails, the last result is not + * cleared. This indicates the assertion failed for the result, so mark it for saving. + */ + @After + fun markArtifactsForSaving() { + lastResult?.flagForSaving() + } + + companion object { + const val TAG = "FLICKER" + const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar" + const val STATUS_BAR_WINDOW_TITLE = "StatusBar" + const val DOCKED_STACK_DIVIDER = "DockedStackDivider" + private val transitionResults = mutableMapOf<String, List<TransitionResult>>() + + /** + * Teardown any system settings and clean up test artifacts from the file system. + * + * Note: test artifacts for failed tests will remain on the device. + */ + @AfterClass + @JvmStatic + fun teardown() { + AutomationUtils.setDefaultWait() + transitionResults.values + .flatten() + .forEach { + if (it.canDelete()) { + it.delete() + } else { + if (it.layersTraceExists()) { + Log.e(TAG, "Layers trace saved to ${it.layersTracePath}") + } + if (it.windowManagerTraceExists()) { + Log.e(TAG, + "WindowManager trace saved to ${it.windowManagerTracePath}") + } + if (it.screenCaptureVideoExists()) { + Log.e(TAG, + "Screen capture video saved to ${it.screenCaptureVideoPath()}") + } + } + } + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java deleted file mode 100644 index 54941dc0f585..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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.flicker; - -import static android.view.Surface.rotationToString; - -import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds; - -import android.graphics.Rect; -import android.view.Surface; - -import androidx.test.filters.FlakyTest; - -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runners.Parameterized.Parameters; - -import java.util.ArrayList; -import java.util.Collection; - -public abstract class NonRotationTestBase extends FlickerTestBase { - - int mBeginRotation; - - public NonRotationTestBase(String beginRotationName, int beginRotation) { - this.mBeginRotation = beginRotation; - } - - @Parameters(name = "{0}") - public static Collection<Object[]> getParams() { - int[] supportedRotations = - {Surface.ROTATION_0, Surface.ROTATION_90}; - Collection<Object[]> params = new ArrayList<>(); - - for (int begin : supportedRotations) { - params.add(new Object[]{rotationToString(begin), begin}); - } - - return params; - } - - @FlakyTest(bugId = 141361128) - @Ignore("Waiting bug feedback") - @Test - public void checkCoveredRegion_noUncoveredRegions() { - Rect displayBounds = getDisplayBounds(mBeginRotation); - checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion( - displayBounds).forAllEntries()); - } - - @FlakyTest(bugId = 141361128) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_navBarLayerIsAlwaysVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()); - } - - @FlakyTest(bugId = 141361128) - @Ignore("Waiting bug feedback") - @Test - public void checkVisibility_statusBarLayerIsAlwaysVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt new file mode 100644 index 000000000000..653fecd5219f --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker + +import android.view.Surface +import androidx.test.filters.FlakyTest +import org.junit.Ignore +import org.junit.Test +import org.junit.runners.Parameterized + +abstract class NonRotationTestBase( + beginRotationName: String, + protected val beginRotation: Int +) : FlickerTestBase() { + @FlakyTest(bugId = 141361128) + @Ignore("Waiting bug feedback") + @Test + fun checkCoveredRegion_noUncoveredRegions() { + val displayBounds = WindowUtils.getDisplayBounds(beginRotation) + checkResults { + LayersTraceSubject.assertThat(it).coversRegion( + displayBounds).forAllEntries() + } + } + + @FlakyTest(bugId = 141361128) + @Ignore("Waiting bug feedback") + @Test + fun checkVisibility_navBarLayerIsAlwaysVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries() + } + } + + @FlakyTest(bugId = 141361128) + @Ignore("Waiting bug feedback") + @Test + fun checkVisibility_statusBarLayerIsAlwaysVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries() + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + val params: MutableCollection<Array<Any>> = ArrayList() + for (begin in supportedRotations) { + params.add(arrayOf(Surface.rotationToString(begin), begin)) + } + return params + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java deleted file mode 100644 index c150e0994316..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java +++ /dev/null @@ -1,84 +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 com.android.server.wm.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.openAppCold; -import static com.android.server.wm.flicker.WmTraceSubject.assertThat; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -/** - * Test cold launch app from launcher. - * To run this test: {@code atest FlickerTests:OpenAppColdTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class OpenAppColdTest extends NonRotationTestBase { - - public OpenAppColdTest(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - - this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(), - "com.android.server.wm.flicker.testapp", "SimpleApp"); - } - - @Override - TransitionRunner getTransitionToRun() { - return openAppCold(mTestApp, mUiDevice, mBeginRotation) - .includeJankyRuns().build(); - } - - @Test - public void checkVisibility_wallpaperWindowBecomesInvisible() { - checkResults(result -> assertThat(result) - .showsBelowAppWindow("Wallpaper") - .then() - .hidesBelowAppWindow("Wallpaper") - .forAllEntries()); - } - - @FlakyTest(bugId = 140855415) - @Ignore("Waiting bug feedback") - @Test - public void checkZOrder_appWindowReplacesLauncherAsTopWindow() { - checkResults(result -> assertThat(result) - .showsAppWindowOnTop( - "com.android.launcher3/.Launcher") - .then() - .showsAppWindowOnTop(mTestApp.getPackage()) - .forAllEntries()); - } - - @Test - public void checkVisibility_wallpaperLayerBecomesInvisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer("Wallpaper") - .then() - .replaceVisibleLayer("Wallpaper", mTestApp.getPackage()) - .forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java deleted file mode 100644 index 00da62fe2ee5..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java +++ /dev/null @@ -1,73 +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 com.android.server.wm.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.LargeTest; - -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -/** - * Test open app to split screen. - * To run this test: {@code atest FlickerTests:OpenAppToSplitScreenTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class OpenAppToSplitScreenTest extends NonRotationTestBase { - - public OpenAppToSplitScreenTest(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - - this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(), - "com.android.server.wm.flicker.testapp", "SimpleApp"); - } - - @Override - TransitionRunner getTransitionToRun() { - return appToSplitScreen(mTestApp, mUiDevice, mBeginRotation) - .includeJankyRuns() - .build(); - } - - @Test - public void checkVisibility_navBarWindowIsAlwaysVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()); - } - - @Test - public void checkVisibility_statusBarWindowIsAlwaysVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries()); - } - - @Test - public void checkVisibility_dividerLayerBecomesVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .hidesLayer(DOCKED_STACK_DIVIDER) - .then() - .showsLayer(DOCKED_STACK_DIVIDER) - .forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java deleted file mode 100644 index 62ae254709b1..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java +++ /dev/null @@ -1,84 +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 com.android.server.wm.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.openAppWarm; -import static com.android.server.wm.flicker.WmTraceSubject.assertThat; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -/** - * Test warm launch app. - * To run this test: {@code atest FlickerTests:OpenAppWarmTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class OpenAppWarmTest extends NonRotationTestBase { - - public OpenAppWarmTest(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - - this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(), - "com.android.server.wm.flicker.testapp", "SimpleApp"); - } - - @Override - TransitionRunner getTransitionToRun() { - return openAppWarm(mTestApp, mUiDevice, mBeginRotation) - .includeJankyRuns().build(); - } - - @Test - public void checkVisibility_wallpaperBecomesInvisible() { - checkResults(result -> assertThat(result) - .showsBelowAppWindow("Wallpaper") - .then() - .hidesBelowAppWindow("Wallpaper") - .forAllEntries()); - } - - @FlakyTest(bugId = 140855415) - @Ignore("Waiting bug feedback") - @Test - public void checkZOrder_appWindowReplacesLauncherAsTopWindow() { - checkResults(result -> assertThat(result) - .showsAppWindowOnTop( - "com.android.launcher3/.Launcher") - .then() - .showsAppWindowOnTop(mTestApp.getPackage()) - .forAllEntries()); - } - - @Test - public void checkVisibility_wallpaperLayerBecomesInvisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer("Wallpaper") - .then() - .replaceVisibleLayer("Wallpaper", mTestApp.getPackage()) - .forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java deleted file mode 100644 index fcb022c280da..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java +++ /dev/null @@ -1,73 +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 com.android.server.wm.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.editTextSetFocus; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.LargeTest; - -import com.android.server.wm.flicker.helpers.ImeAppHelper; - -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -/** - * Test IME window opening transitions. - * To run this test: {@code atest FlickerTests:OpenImeWindowTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class OpenImeWindowTest extends NonRotationTestBase { - - private static final String IME_WINDOW_TITLE = "InputMethod"; - - public OpenImeWindowTest(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - - mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation()); - } - - @Override - TransitionRunner getTransitionToRun() { - return editTextSetFocus((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation) - .includeJankyRuns().build(); - } - - @Test - public void checkVisibility_imeWindowBecomesVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .skipUntilFirstAssertion() - .hidesImeWindow(IME_WINDOW_TITLE) - .then() - .showsImeWindow(IME_WINDOW_TITLE) - .forAllEntries()); - } - - @Test - public void checkVisibility_imeLayerBecomesVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .hidesLayer(IME_WINDOW_TITLE) - .then() - .showsLayer(IME_WINDOW_TITLE) - .forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/PipTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/PipTestBase.java deleted file mode 100644 index cf51d780d7d2..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/PipTestBase.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker; - -import static com.android.server.wm.flicker.helpers.AutomationUtils.closePipWindow; -import static com.android.server.wm.flicker.helpers.AutomationUtils.hasPipWindow; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.LargeTest; -import androidx.test.uiautomator.UiDevice; - -import com.android.server.wm.flicker.helpers.PipAppHelper; - -import org.junit.AfterClass; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public abstract class PipTestBase extends NonRotationTestBase { - static final String sPipWindowTitle = "PipMenuActivity"; - - public PipTestBase(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - - this.mTestApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation()); - } - - @AfterClass - public static void teardown() { - UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - - if (hasPipWindow(device)) { - closePipWindow(device); - } - } - - @Test - public void checkVisibility_pipWindowBecomesVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .skipUntilFirstAssertion() - .showsAppWindowOnTop(sPipWindowTitle) - .then() - .hidesAppWindow(sPipWindowTitle) - .forAllEntries()); - } - - @Test - public void checkVisibility_pipLayerBecomesVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .skipUntilFirstAssertion() - .showsLayer(sPipWindowTitle) - .then() - .hidesLayer(sPipWindowTitle) - .forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java deleted file mode 100644 index 5e67ada5ff55..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.exitPipModeToApp; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; - -import com.android.server.wm.flicker.helpers.PipAppHelper; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -/** - * Test Pip launch. - * To run this test: {@code atest FlickerTests:PipToAppTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 152738416) -@Ignore("Waiting bug feedback") -public class PipToAppTest extends PipTestBase { - public PipToAppTest(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - } - - @Override - TransitionRunner getTransitionToRun() { - return exitPipModeToApp((PipAppHelper) mTestApp, mUiDevice, mBeginRotation) - .includeJankyRuns().build(); - } - - @Test - public void checkVisibility_backgroundWindowVisibleBehindPipLayer() { - checkResults(result -> WmTraceSubject.assertThat(result) - .skipUntilFirstAssertion() - .showsAppWindowOnTop(sPipWindowTitle) - .then() - .showsBelowAppWindow("Wallpaper") - .then() - .showsAppWindowOnTop(mTestApp.getPackage()) - .then() - .hidesAppWindowOnTop(mTestApp.getPackage()) - .forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java deleted file mode 100644 index af713c7b39b7..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.exitPipModeToHome; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; - -import com.android.server.wm.flicker.helpers.PipAppHelper; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; - -/** - * Test Pip launch. - * To run this test: {@code atest FlickerTests:PipToHomeTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 152738416) -@Ignore("Waiting bug feedback") -public class PipToHomeTest extends PipTestBase { - public PipToHomeTest(String beginRotationName, int beginRotation) { - super(beginRotationName, beginRotation); - } - - @Override - TransitionRunner getTransitionToRun() { - return exitPipModeToHome((PipAppHelper) mTestApp, mUiDevice, mBeginRotation) - .includeJankyRuns().build(); - } - - @Ignore - @Test - public void checkVisibility_backgroundWindowVisibleBehindPipLayer() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAppWindowOnTop(sPipWindowTitle) - .then() - .showsBelowAppWindow("Wallpaper") - .then() - .showsAppWindowOnTop("Wallpaper") - .forAllEntries()); - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java deleted file mode 100644 index 9516af624f6f..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java +++ /dev/null @@ -1,200 +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 com.android.server.wm.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.resizeSplitScreen; -import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds; -import static com.android.server.wm.flicker.WindowUtils.getDockedStackDividerInset; -import static com.android.server.wm.flicker.WindowUtils.getNavigationBarHeight; -import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen; -import static com.android.server.wm.flicker.helpers.AutomationUtils.isInSplitScreen; - -import static com.google.common.truth.Truth.assertThat; - -import android.app.Instrumentation; -import android.graphics.Rect; -import android.util.Rational; -import android.view.Surface; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; -import androidx.test.uiautomator.UiDevice; - -import com.android.server.wm.flicker.helpers.ImeAppHelper; - -import org.junit.AfterClass; -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; - -/** - * Test split screen resizing window transitions. - * To run this test: {@code atest FlickerTests:ResizeSplitScreenTest} - * - * Currently it runs only in 0 degrees because of b/156100803 - */ -@LargeTest -@RunWith(AndroidJUnit4.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 159096424) -@Ignore("Waiting bug feedback") -public class ResizeSplitScreenTest extends FlickerTestBase { - - private static String sSimpleActivity = "SimpleActivity"; - private static String sImeActivity = "ImeActivity"; - - public ResizeSplitScreenTest() { - this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(), - "com.android.server.wm.flicker.testapp", "SimpleApp"); - } - - @Override - TransitionRunner getTransitionToRun() { - Instrumentation instr = InstrumentationRegistry.getInstrumentation(); - ImeAppHelper bottomApp = new ImeAppHelper(instr); - return resizeSplitScreen(instr, mTestApp, bottomApp, mUiDevice, Surface.ROTATION_0, - new Rational(1, 3), new Rational(2, 3)) - .includeJankyRuns().build(); - } - - @Test - public void checkVisibility_topAppLayerIsAlwaysVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(sSimpleActivity) - .forAllEntries()); - } - - @Test - public void checkVisibility_bottomAppLayerIsAlwaysVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(sImeActivity) - .forAllEntries()); - } - - @Test - public void checkVisibility_dividerLayerIsAlwaysVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(DOCKED_STACK_DIVIDER) - .forAllEntries()); - } - - @Test - @Ignore("Waiting feedback") - public void checkPosition_appsStartingBounds() { - Rect displayBounds = getDisplayBounds(); - checkResults(result -> { - LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(), - result.getLayersTracePath(), result.getLayersTraceChecksum()); - - assertThat(entries.getEntries()).isNotEmpty(); - Rect startingDividerBounds = entries.getEntries().get(0).getVisibleBounds - (DOCKED_STACK_DIVIDER); - - Rect startingTopAppBounds = new Rect(0, 0, startingDividerBounds.right, - startingDividerBounds.top + getDockedStackDividerInset()); - - Rect startingBottomAppBounds = new Rect(0, - startingDividerBounds.bottom - getDockedStackDividerInset(), - displayBounds.right, - displayBounds.bottom - getNavigationBarHeight()); - - LayersTraceSubject.assertThat(result) - .hasVisibleRegion("SimpleActivity", startingTopAppBounds) - .inTheBeginning(); - - LayersTraceSubject.assertThat(result) - .hasVisibleRegion("ImeActivity", startingBottomAppBounds) - .inTheBeginning(); - }); - } - - @Test - @Ignore("Waiting feedback") - public void checkPosition_appsEndingBounds() { - Rect displayBounds = getDisplayBounds(); - checkResults(result -> { - LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(), - result.getLayersTracePath(), result.getLayersTraceChecksum()); - - assertThat(entries.getEntries()).isNotEmpty(); - Rect endingDividerBounds = entries.getEntries().get( - entries.getEntries().size() - 1).getVisibleBounds( - DOCKED_STACK_DIVIDER); - - Rect startingTopAppBounds = new Rect(0, 0, endingDividerBounds.right, - endingDividerBounds.top + getDockedStackDividerInset()); - - Rect startingBottomAppBounds = new Rect(0, - endingDividerBounds.bottom - getDockedStackDividerInset(), - displayBounds.right, - displayBounds.bottom - getNavigationBarHeight()); - - LayersTraceSubject.assertThat(result) - .hasVisibleRegion(sSimpleActivity, startingTopAppBounds) - .atTheEnd(); - - LayersTraceSubject.assertThat(result) - .hasVisibleRegion(sImeActivity, startingBottomAppBounds) - .atTheEnd(); - }); - } - - @Test - public void checkVisibility_navBarWindowIsAlwaysVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE) - .forAllEntries()); - } - - @Test - public void checkVisibility_statusBarWindowIsAlwaysVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE) - .forAllEntries()); - } - - @Test - @FlakyTest(bugId = 156223549) - @Ignore("Waiting bug feedback") - public void checkVisibility_topAppWindowIsAlwaysVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAppWindow(sSimpleActivity) - .forAllEntries()); - } - - @Test - @FlakyTest(bugId = 156223549) - @Ignore("Waiting bug feedback") - public void checkVisibility_bottomAppWindowIsAlwaysVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAppWindow(sImeActivity) - .forAllEntries()); - } - - @AfterClass - public static void teardown() { - UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - - if (isInSplitScreen(device)) { - exitSplitScreen(device); - } - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt new file mode 100644 index 000000000000..873d60771cb4 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker + +import android.view.Surface +import androidx.test.filters.FlakyTest +import org.junit.Ignore +import org.junit.Test +import org.junit.runners.Parameterized + +abstract class RotationTestBase( + beginRotationName: String, + endRotationName: String, + protected val beginRotation: Int, + protected val endRotation: Int +) : FlickerTestBase() { + @FlakyTest(bugId = 140855415) + @Ignore("Waiting bug feedback") + @Test + fun checkVisibility_navBarWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries() + } + } + + @FlakyTest(bugId = 140855415) + @Ignore("Waiting bug feedback") + @Test + fun checkVisibility_statusBarWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries() + } + } + + @Test + fun checkPosition_navBarLayerRotatesAndScales() { + val startingPos = WindowUtils.getNavigationBarPosition(beginRotation) + val endingPos = WindowUtils.getNavigationBarPosition(endRotation) + if (startingPos == endingPos) { + checkResults { + LayersTraceSubject.assertThat(it) + .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos) + .forAllEntries() + } + } else { + checkResults { + LayersTraceSubject.assertThat(it) + .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos) + .inTheBeginning() + } + checkResults { + LayersTraceSubject.assertThat(it) + .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos) + .atTheEnd() + } + } + } + + @Test + fun checkPosition_statusBarLayerRotatesScales() { + val startingPos = WindowUtils.getStatusBarPosition(beginRotation) + val endingPos = WindowUtils.getStatusBarPosition(endRotation) + checkResults { + LayersTraceSubject.assertThat(it) + .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos) + .inTheBeginning() + LayersTraceSubject.assertThat(it) + .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos).atTheEnd() + } + } + + @FlakyTest(bugId = 140855415) + @Ignore("Waiting bug feedback") + @Test + fun checkVisibility_navBarLayerIsAlwaysVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries() + } + } + + @FlakyTest(bugId = 140855415) + @Ignore("Waiting bug feedback") + @Test + fun checkVisibility_statusBarLayerIsAlwaysVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries() + } + } + + companion object { + const val SCREENSHOT_LAYER = "RotationLayer" + + @Parameterized.Parameters(name = "{0}-{1}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + val params: MutableCollection<Array<Any>> = mutableListOf() + for (begin in supportedRotations) { + for (end in supportedRotations) { + if (begin != end) { + params.add(arrayOf( + Surface.rotationToString(begin), + Surface.rotationToString(end), + begin, + end + )) + } + } + } + return params + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java deleted file mode 100644 index 3cff8683eca5..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java +++ /dev/null @@ -1,175 +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 com.android.server.wm.flicker; - -import static android.view.Surface.rotationToString; - -import static com.android.server.wm.flicker.CommonTransitions.changeAppRotation; -import static com.android.server.wm.flicker.WindowUtils.getAppPosition; -import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds; -import static com.android.server.wm.flicker.WindowUtils.getNavigationBarPosition; -import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD; -import static com.android.server.wm.flicker.testapp.ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME; - -import android.content.Intent; -import android.graphics.Rect; -import android.view.Surface; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; - -import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.ArrayList; -import java.util.Collection; - -/** - * Cycle through supported app rotations using seamless rotations. - * To run this test: {@code atest FlickerTests:SeamlessAppRotationTest} - */ -@LargeTest -@RunWith(Parameterized.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 147659548) -@Ignore("Waiting bug feedback") -public class SeamlessAppRotationTest extends FlickerTestBase { - private int mBeginRotation; - private int mEndRotation; - private Intent mIntent; - - public SeamlessAppRotationTest(String testId, Intent intent, int beginRotation, - int endRotation) { - this.mIntent = intent; - this.mBeginRotation = beginRotation; - this.mEndRotation = endRotation; - } - - @Parameters(name = "{0}") - public static Collection<Object[]> getParams() { - int[] supportedRotations = - {Surface.ROTATION_0, Surface.ROTATION_90}; - Collection<Object[]> params = new ArrayList<>(); - - ArrayList<Intent> testIntents = new ArrayList<>(); - - // launch test activity that supports seamless rotation - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setComponent(SEAMLESS_ACTIVITY_COMPONENT_NAME); - testIntents.add(intent); - - // launch test activity that supports seamless rotation with a busy UI thread to miss frames - // when the app is asked to redraw - intent = new Intent(intent); - intent.putExtra(EXTRA_STARVE_UI_THREAD, true); - testIntents.add(intent); - - for (Intent testIntent : testIntents) { - for (int begin : supportedRotations) { - for (int end : supportedRotations) { - if (begin != end) { - String testId = rotationToString(begin) + "_" + rotationToString(end); - if (testIntent.getExtras() != null && - testIntent.getExtras().getBoolean(EXTRA_STARVE_UI_THREAD)) { - testId += "_" + "BUSY_UI_THREAD"; - } - params.add(new Object[]{testId, testIntent, begin, end}); - } - } - } - } - return params; - } - - @Override - TransitionRunner getTransitionToRun() { - String intentId = ""; - if (mIntent.getExtras() != null && - mIntent.getExtras().getBoolean(EXTRA_STARVE_UI_THREAD)) { - intentId = "BUSY_UI_THREAD"; - } - - return changeAppRotation(mIntent, intentId, InstrumentationRegistry.getContext(), - mUiDevice, mBeginRotation, mEndRotation).build(); - } - - @Test - public void checkVisibility_navBarWindowIsAlwaysVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()); - } - - @Test - public void checkPosition_navBarLayerRotatesAndScales() { - Rect startingPos = getNavigationBarPosition(mBeginRotation); - Rect endingPos = getNavigationBarPosition(mEndRotation); - if (startingPos.equals(endingPos)) { - checkResults(result -> LayersTraceSubject.assertThat(result) - .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos) - .forAllEntries()); - } else { - checkResults(result -> LayersTraceSubject.assertThat(result) - .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos) - .inTheBeginning()); - checkResults(result -> LayersTraceSubject.assertThat(result) - .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos) - .atTheEnd()); - } - } - - @Test - public void checkPosition_appLayerRotates() { - Rect startingPos = getAppPosition(mBeginRotation); - Rect endingPos = getAppPosition(mEndRotation); - if (startingPos.equals(endingPos)) { - checkResults(result -> LayersTraceSubject.assertThat(result) - .hasVisibleRegion(mIntent.getComponent().getPackageName(), startingPos) - .forAllEntries()); - } else { - checkResults(result -> LayersTraceSubject.assertThat(result) - .hasVisibleRegion(mIntent.getComponent().getPackageName(), startingPos) - .then() - .hasVisibleRegion(mIntent.getComponent().getPackageName(), endingPos) - .forAllEntries()); - } - } - - @Test - public void checkCoveredRegion_noUncoveredRegions() { - Rect startingBounds = getDisplayBounds(mBeginRotation); - Rect endingBounds = getDisplayBounds(mEndRotation); - if (startingBounds.equals(endingBounds)) { - checkResults(result -> - LayersTraceSubject.assertThat(result) - .coversRegion(startingBounds) - .forAllEntries()); - } else { - checkResults(result -> - LayersTraceSubject.assertThat(result) - .coversRegion(startingBounds) - .then() - .coversRegion(endingBounds) - .forAllEntries()); - } - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java deleted file mode 100644 index 9e9c82903b51..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java +++ /dev/null @@ -1,102 +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 com.android.server.wm.flicker; - -import static com.android.server.wm.flicker.CommonTransitions.splitScreenToLauncher; -import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds; -import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen; -import static com.android.server.wm.flicker.helpers.AutomationUtils.isInSplitScreen; - -import android.view.Surface; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; -import androidx.test.uiautomator.UiDevice; - -import org.junit.AfterClass; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; - -/** - * Test open app to split screen. - * To run this test: {@code atest FlickerTests:SplitScreenToLauncherTest} - */ -@LargeTest -@RunWith(AndroidJUnit4.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class SplitScreenToLauncherTest extends FlickerTestBase { - - public SplitScreenToLauncherTest() { - this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(), - "com.android.server.wm.flicker.testapp", "SimpleApp"); - } - - @Override - TransitionRunner getTransitionToRun() { - return splitScreenToLauncher(mTestApp, mUiDevice, Surface.ROTATION_0) - .includeJankyRuns().build(); - } - - @Test - public void checkCoveredRegion_noUncoveredRegions() { - checkResults(result -> - LayersTraceSubject.assertThat(result) - .coversRegion(getDisplayBounds()).forAllEntries()); - } - - @Test - public void checkVisibility_dividerLayerBecomesInVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(DOCKED_STACK_DIVIDER) - .then() - .hidesLayer(DOCKED_STACK_DIVIDER) - .forAllEntries()); - } - - @Test - public void checkVisibility_appLayerBecomesInVisible() { - checkResults(result -> LayersTraceSubject.assertThat(result) - .showsLayer(mTestApp.getPackage()) - .then() - .hidesLayer(mTestApp.getPackage()) - .forAllEntries()); - } - - @Test - public void checkVisibility_navBarWindowIsAlwaysVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()); - } - - @Test - public void checkVisibility_statusBarWindowIsAlwaysVisible() { - checkResults(result -> WmTraceSubject.assertThat(result) - .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries()); - } - - @AfterClass - public static void teardown() { - UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - - if (isInSplitScreen(device)) { - exitSplitScreen(device); - } - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt index 42977f549162..e579533d2bb7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,17 @@ * limitations under the License. */ -package com.android.server.wm.flicker.helpers; +package com.android.server.wm.flicker.helpers -import android.app.Instrumentation; +import android.app.Instrumentation +import com.android.server.wm.flicker.StandardAppHelper -import com.android.server.wm.flicker.StandardAppHelper; - -public abstract class FlickerAppHelper extends StandardAppHelper { - - static int sFindTimeout = 10000; - static String sFlickerPackage = "com.android.server.wm.flicker.testapp"; - - public FlickerAppHelper(Instrumentation instr, String launcherName) { - super(instr, sFlickerPackage, launcherName); +abstract class FlickerAppHelper( + instr: Instrumentation, + launcherName: String +) : StandardAppHelper(instr, sFlickerPackage, launcherName) { + companion object { + var sFindTimeout = 10000 + var sFlickerPackage = "com.android.server.wm.flicker.testapp" } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt index 1b2c48405f6e..d587f1e383c5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,13 @@ * limitations under the License. */ -package com.android.server.wm.flicker.helpers; +package com.android.server.wm.flicker.helpers -import android.app.Instrumentation; +import android.app.Instrumentation +import androidx.test.uiautomator.UiDevice -import androidx.test.uiautomator.UiDevice; - -public class ImeAppAutoFocusHelper extends ImeAppHelper { - - public ImeAppAutoFocusHelper(Instrumentation instr) { - super(instr, "ImeAppAutoFocus"); - } - - public void openIME(UiDevice device) { +class ImeAppAutoFocusHelper(instr: Instrumentation) : ImeAppHelper(instr, "ImeAppAutoFocus") { + override fun openIME(device: UiDevice) { // do nothing (the app is focused automatically) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java deleted file mode 100644 index 095722d59fc7..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.flicker.helpers; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import android.app.Instrumentation; - -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject2; -import androidx.test.uiautomator.Until; - -public class ImeAppHelper extends FlickerAppHelper { - - ImeAppHelper(Instrumentation instr, String launcherName) { - super(instr, launcherName); - } - - public ImeAppHelper(Instrumentation instr) { - this(instr, "ImeApp"); - } - - public void openIME(UiDevice device) { - UiObject2 editText = device.wait( - Until.findObject(By.res(getPackage(), "plain_text_input")), - AutomationUtils.FIND_TIMEOUT); - assertNotNull("Text field not found, this usually happens when the device was left " - + "in an unknown state (e.g. in split screen)", editText); - editText.click(); - - if (!AutomationUtils.waitForIME(device)) { - fail("IME did not appear"); - } - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt new file mode 100644 index 000000000000..979cbeaca539 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.helpers + +import android.app.Instrumentation +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import org.junit.Assert + +open class ImeAppHelper( + instr: Instrumentation, + launcherName: String = "ImeApp" +) : FlickerAppHelper(instr, launcherName) { + open fun openIME(device: UiDevice) { + val editText = device.wait( + Until.findObject(By.res(getPackage(), "plain_text_input")), + AutomationUtils.FIND_TIMEOUT) + Assert.assertNotNull("Text field not found, this usually happens when the device " + + "was left in an unknown state (e.g. in split screen)", editText) + editText.click() + if (!AutomationUtils.waitForIME(device)) { + Assert.fail("IME did not appear") + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java deleted file mode 100644 index f2b7a7e351fc..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.flicker.helpers; - -import static com.android.server.wm.flicker.helpers.AutomationUtils.hasPipWindow; - -import static org.junit.Assert.assertNotNull; - -import android.app.Instrumentation; - -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject2; - -public class PipAppHelper extends FlickerAppHelper { - - public PipAppHelper(Instrumentation instr) { - super(instr, "PipApp"); - } - - public void clickEnterPipButton(UiDevice device) { - UiObject2 enterPipButton = device.findObject(By.res(getPackage(), "enter_pip")); - assertNotNull("Pip button not found, this usually happens when the device was left " - + "in an unknown state (e.g. in split screen)", enterPipButton); - enterPipButton.click(); - hasPipWindow(device); - } - - public void closePipWindow(UiDevice device) { - AutomationUtils.closePipWindow(device); - } - -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt new file mode 100644 index 000000000000..daee810b6899 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.helpers + +import android.app.Instrumentation +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import org.junit.Assert + +class PipAppHelper(instr: Instrumentation) : FlickerAppHelper(instr, "PipApp") { + fun clickEnterPipButton(device: UiDevice) { + val enterPipButton = device.findObject(By.res(getPackage(), "enter_pip")) + Assert.assertNotNull("Pip button not found, this usually happens when the device " + + "was left in an unknown state (e.g. in split screen)", enterPipButton) + enterPipButton.click() + AutomationUtils.hasPipWindow(device) + } + + fun closePipWindow(device: UiDevice) { + AutomationUtils.closePipWindow(device) + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt new file mode 100644 index 000000000000..6946618806b8 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.ime + +import androidx.test.filters.FlakyTest +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test IME window closing back to app window transitions. + * To run this test: `atest FlickerTests:CloseImeWindowToAppTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class CloseImeAutoOpenWindowToAppTest( + beginRotationName: String, + beginRotation: Int +) : CloseImeWindowToAppTest(beginRotationName, beginRotation) { + init { + testApp = ImeAppAutoFocusHelper(instrumentation) + } + + override val transitionToRun: TransitionRunner + get() = CommonTransitions.editTextLoseFocusToApp(testApp as ImeAppAutoFocusHelper, + instrumentation, uiDevice, beginRotation) + .includeJankyRuns().build() + + @FlakyTest(bugId = 141458352) + @Ignore("Waiting bug feedback") + @Test + override fun checkVisibility_imeLayerBecomesInvisible() { + super.checkVisibility_imeLayerBecomesInvisible() + } + + @FlakyTest(bugId = 141458352) + @Ignore("Waiting bug feedback") + @Test + override fun checkVisibility_imeAppLayerIsAlwaysVisible() { + super.checkVisibility_imeAppLayerIsAlwaysVisible() + } + + @FlakyTest(bugId = 141458352) + @Ignore("Waiting bug feedback") + @Test + override fun checkVisibility_imeAppWindowIsAlwaysVisible() { + super.checkVisibility_imeAppWindowIsAlwaysVisible() + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt new file mode 100644 index 000000000000..a1030d8248b7 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.ime + +import androidx.test.filters.FlakyTest +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test IME window closing back to app window transitions. + * To run this test: `atest FlickerTests:CloseImeWindowToAppTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class CloseImeAutoOpenWindowToHomeTest( + beginRotationName: String, + beginRotation: Int +) : CloseImeWindowToHomeTest(beginRotationName, beginRotation) { + init { + testApp = ImeAppAutoFocusHelper(instrumentation) + } + + override val transitionToRun: TransitionRunner + get() = CommonTransitions.editTextLoseFocusToHome(testApp as ImeAppAutoFocusHelper, + instrumentation, uiDevice, beginRotation) + .includeJankyRuns().build() + + @FlakyTest(bugId = 141458352) + @Ignore("Waiting bug feedback") + @Test + override fun checkVisibility_imeWindowBecomesInvisible() { + super.checkVisibility_imeWindowBecomesInvisible() + } + + @FlakyTest(bugId = 141458352) + @Ignore("Waiting bug feedback") + @Test + override fun checkVisibility_imeLayerBecomesInvisible() { + super.checkVisibility_imeLayerBecomesInvisible() + } + + @FlakyTest(bugId = 157449248) + @Ignore("Waiting bug feedback") + @Test + override fun checkVisibility_imeAppWindowBecomesInvisible() { + super.checkVisibility_imeAppWindowBecomesInvisible() + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt new file mode 100644 index 000000000000..6e7c92b97bfb --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.ime + +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.NonRotationTestBase +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WmTraceSubject +import com.android.server.wm.flicker.helpers.ImeAppHelper +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test IME window closing back to app window transitions. + * To run this test: `atest FlickerTests:CloseImeWindowToAppTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class CloseImeWindowToAppTest( + beginRotationName: String, + beginRotation: Int +) : NonRotationTestBase(beginRotationName, beginRotation) { + init { + testApp = ImeAppHelper(instrumentation) + } + + override val transitionToRun: TransitionRunner + get() = CommonTransitions.editTextLoseFocusToApp(testApp as ImeAppHelper, + instrumentation, uiDevice, beginRotation) + .includeJankyRuns().build() + + @Ignore("Flaky. Pending debug") + @Test + open fun checkVisibility_imeLayerBecomesInvisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(IME_WINDOW_TITLE) + .then() + .hidesLayer(IME_WINDOW_TITLE) + .forAllEntries() + } + } + + @Test + open fun checkVisibility_imeAppLayerIsAlwaysVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(testApp.getPackage()) + .forAllEntries() + } + } + + @Test + open fun checkVisibility_imeAppWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAppWindowOnTop(testApp.getPackage()) + .forAllEntries() + } + } + + companion object { + const val IME_WINDOW_TITLE = "InputMethod" + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt new file mode 100644 index 000000000000..7b155ebd93e1 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.ime + +import androidx.test.filters.FlakyTest +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.NonRotationTestBase +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WmTraceSubject +import com.android.server.wm.flicker.helpers.ImeAppHelper +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test IME window closing to home transitions. + * To run this test: `atest FlickerTests:CloseImeWindowToHomeTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class CloseImeWindowToHomeTest( + beginRotationName: String, + beginRotation: Int +) : NonRotationTestBase(beginRotationName, beginRotation) { + init { + testApp = ImeAppHelper(instrumentation) + } + + override val transitionToRun: TransitionRunner + get() = CommonTransitions.editTextLoseFocusToHome(testApp as ImeAppHelper, + instrumentation, uiDevice, beginRotation) + .includeJankyRuns().build() + + @Test + open fun checkVisibility_imeWindowBecomesInvisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsImeWindow(IME_WINDOW_TITLE) + .then() + .hidesImeWindow(IME_WINDOW_TITLE) + .forAllEntries() + } + } + + @FlakyTest(bugId = 153739621) + @Ignore + @Test + open fun checkVisibility_imeLayerBecomesInvisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .skipUntilFirstAssertion() + .showsLayer(IME_WINDOW_TITLE) + .then() + .hidesLayer(IME_WINDOW_TITLE) + .forAllEntries() + } + } + + @FlakyTest(bugId = 153739621) + @Ignore + @Test + fun checkVisibility_imeAppLayerBecomesInvisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .skipUntilFirstAssertion() + .showsLayer(testApp.getPackage()) + .then() + .hidesLayer(testApp.getPackage()) + .forAllEntries() + } + } + + @Test + open fun checkVisibility_imeAppWindowBecomesInvisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAppWindowOnTop(testApp.getPackage()) + .then() + .hidesAppWindowOnTop(testApp.getPackage()) + .forAllEntries() + } + } + + companion object { + const val IME_WINDOW_TITLE: String = "InputMethod" + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt new file mode 100644 index 000000000000..9885359a0fb7 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.ime + +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.NonRotationTestBase +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WmTraceSubject +import com.android.server.wm.flicker.helpers.ImeAppHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test IME window opening transitions. + * To run this test: `atest FlickerTests:OpenImeWindowTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class OpenImeWindowTest( + beginRotationName: String, + beginRotation: Int +) : NonRotationTestBase(beginRotationName, beginRotation) { + init { + testApp = ImeAppHelper(instrumentation) + } + + override val transitionToRun: TransitionRunner + get() = CommonTransitions.editTextSetFocus(testApp as ImeAppHelper, + instrumentation, uiDevice, beginRotation) + .includeJankyRuns().build() + + @Test + fun checkVisibility_imeWindowBecomesVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .skipUntilFirstAssertion() + .hidesImeWindow(IME_WINDOW_TITLE) + .then() + .showsImeWindow(IME_WINDOW_TITLE) + .forAllEntries() + } + } + + @Test + fun checkVisibility_imeLayerBecomesVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .hidesLayer(IME_WINDOW_TITLE) + .then() + .showsLayer(IME_WINDOW_TITLE) + .forAllEntries() + } + } + + companion object { + private const val IME_WINDOW_TITLE = "InputMethod" + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt new file mode 100644 index 000000000000..943a52592910 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.launch + +import androidx.test.filters.FlakyTest +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.NonRotationTestBase +import com.android.server.wm.flicker.StandardAppHelper +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WmTraceSubject +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test cold launch app from launcher. + * To run this test: `atest FlickerTests:OpenAppColdTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class OpenAppColdTest( + beginRotationName: String, + beginRotation: Int +) : NonRotationTestBase(beginRotationName, beginRotation) { + init { + testApp = StandardAppHelper(instrumentation, + "com.android.server.wm.flicker.testapp", "SimpleApp") + } + + override val transitionToRun: TransitionRunner + get() = CommonTransitions.openAppCold(testApp, instrumentation, uiDevice, beginRotation) + .includeJankyRuns().build() + + @Test + fun checkVisibility_wallpaperWindowBecomesInvisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsBelowAppWindow("Wallpaper") + .then() + .hidesBelowAppWindow("Wallpaper") + .forAllEntries() + } + } + + @FlakyTest(bugId = 140855415) + @Ignore("Waiting bug feedback") + @Test + fun checkZOrder_appWindowReplacesLauncherAsTopWindow() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAppWindowOnTop( + "com.android.launcher3/.Launcher") + .then() + .showsAppWindowOnTop(testApp.getPackage()) + .forAllEntries() + } + } + + @Test + fun checkVisibility_wallpaperLayerBecomesInvisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer("Wallpaper") + .then() + .replaceVisibleLayer("Wallpaper", testApp.getPackage()) + .forAllEntries() + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt new file mode 100644 index 000000000000..7964d949e737 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.launch + +import androidx.test.filters.FlakyTest +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.NonRotationTestBase +import com.android.server.wm.flicker.StandardAppHelper +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WmTraceSubject +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test warm launch app. + * To run this test: `atest FlickerTests:OpenAppWarmTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class OpenAppWarmTest( + beginRotationName: String, + beginRotation: Int +) : NonRotationTestBase(beginRotationName, beginRotation) { + init { + testApp = StandardAppHelper(instrumentation, + "com.android.server.wm.flicker.testapp", "SimpleApp") + } + + override val transitionToRun: TransitionRunner + get() = CommonTransitions.openAppWarm(testApp, instrumentation, uiDevice, beginRotation) + .includeJankyRuns().build() + + @Test + fun checkVisibility_wallpaperBecomesInvisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsBelowAppWindow("Wallpaper") + .then() + .hidesBelowAppWindow("Wallpaper") + .forAllEntries() + } + } + + @FlakyTest(bugId = 140855415) + @Ignore("Waiting bug feedback") + @Test + fun checkZOrder_appWindowReplacesLauncherAsTopWindow() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAppWindowOnTop( + "com.android.launcher3/.Launcher") + .then() + .showsAppWindowOnTop(testApp.getPackage()) + .forAllEntries() + } + } + + @Test + fun checkVisibility_wallpaperLayerBecomesInvisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer("Wallpaper") + .then() + .replaceVisibleLayer("Wallpaper", testApp.getPackage()) + .forAllEntries() + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt new file mode 100644 index 000000000000..79321f9488ad --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.pip + +import androidx.test.InstrumentationRegistry +import androidx.test.filters.LargeTest +import androidx.test.uiautomator.UiDevice +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.NonRotationTestBase +import com.android.server.wm.flicker.WmTraceSubject +import com.android.server.wm.flicker.helpers.AutomationUtils +import com.android.server.wm.flicker.helpers.PipAppHelper +import org.junit.AfterClass +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +abstract class PipTestBase( + beginRotationName: String, + beginRotation: Int +) : NonRotationTestBase(beginRotationName, beginRotation) { + init { + testApp = PipAppHelper(instrumentation) + } + + @Test + fun checkVisibility_pipWindowBecomesVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .skipUntilFirstAssertion() + .showsAppWindowOnTop(sPipWindowTitle) + .then() + .hidesAppWindow(sPipWindowTitle) + .forAllEntries() + } + } + + @Test + fun checkVisibility_pipLayerBecomesVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .skipUntilFirstAssertion() + .showsLayer(sPipWindowTitle) + .then() + .hidesLayer(sPipWindowTitle) + .forAllEntries() + } + } + + companion object { + const val sPipWindowTitle = "PipMenuActivity" + + @AfterClass + @JvmStatic + fun teardown() { + val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + if (AutomationUtils.hasPipWindow(device)) { + AutomationUtils.closePipWindow(device) + } + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt new file mode 100644 index 000000000000..bfececa72d4e --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.pip + +import androidx.test.filters.FlakyTest +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WmTraceSubject +import com.android.server.wm.flicker.helpers.PipAppHelper +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip launch. + * To run this test: `atest FlickerTests:PipToAppTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 152738416) +@Ignore("Waiting bug feedback") +class PipToAppTest( + beginRotationName: String, + beginRotation: Int +) : PipTestBase(beginRotationName, beginRotation) { + override val transitionToRun: TransitionRunner + get() = CommonTransitions.exitPipModeToApp(testApp as PipAppHelper, instrumentation, + uiDevice, beginRotation) + .includeJankyRuns().build() + + @Test + fun checkVisibility_backgroundWindowVisibleBehindPipLayer() { + checkResults { + WmTraceSubject.assertThat(it) + .skipUntilFirstAssertion() + .showsAppWindowOnTop(sPipWindowTitle) + .then() + .showsBelowAppWindow("Wallpaper") + .then() + .showsAppWindowOnTop(testApp.getPackage()) + .then() + .hidesAppWindowOnTop(testApp.getPackage()) + .forAllEntries() + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt new file mode 100644 index 000000000000..ecfcd8298c3f --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.pip + +import androidx.test.filters.FlakyTest +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WmTraceSubject +import com.android.server.wm.flicker.helpers.PipAppHelper +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip launch. + * To run this test: `atest FlickerTests:PipToHomeTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 152738416) +@Ignore("Waiting bug feedback") +class PipToHomeTest( + beginRotationName: String, + beginRotation: Int +) : PipTestBase(beginRotationName, beginRotation) { + override val transitionToRun: TransitionRunner + get() = CommonTransitions.exitPipModeToHome(testApp as PipAppHelper, instrumentation, + uiDevice, beginRotation) + .includeJankyRuns().build() + + @Ignore + @Test + fun checkVisibility_backgroundWindowVisibleBehindPipLayer() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAppWindowOnTop(sPipWindowTitle) + .then() + .showsBelowAppWindow("Wallpaper") + .then() + .showsAppWindowOnTop("Wallpaper") + .forAllEntries() + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt new file mode 100644 index 000000000000..7a581d066c2b --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.rotation + +import android.util.Log +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.RotationTestBase +import com.android.server.wm.flicker.StandardAppHelper +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WindowUtils +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Cycle through supported app rotations. + * To run this test: `atest FlickerTest:ChangeAppRotationTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class ChangeAppRotationTest( + beginRotationName: String, + endRotationName: String, + beginRotation: Int, + endRotation: Int +) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) { + init { + testApp = StandardAppHelper(instrumentation, + "com.android.server.wm.flicker.testapp", "SimpleApp") + } + + override val transitionToRun: TransitionRunner + get() = CommonTransitions.changeAppRotation(testApp, instrumentation, uiDevice, + beginRotation, endRotation) + .includeJankyRuns().build() + + @Test + fun checkPosition_appLayerRotates() { + val startingPos = WindowUtils.getAppPosition(beginRotation) + val endingPos = WindowUtils.getAppPosition(endRotation) + Log.e(TAG, "startingPos=$startingPos endingPos=$endingPos") + checkResults { + LayersTraceSubject.assertThat(it) + .hasVisibleRegion(testApp.getPackage(), startingPos).inTheBeginning() + LayersTraceSubject.assertThat(it) + .hasVisibleRegion(testApp.getPackage(), endingPos).atTheEnd() + } + } + + @Ignore("Flaky. Pending debug") + @Test + fun checkVisibility_screenshotLayerBecomesInvisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(testApp.getPackage()) + .then() + .replaceVisibleLayer(testApp.getPackage(), SCREENSHOT_LAYER) + .then() + .showsLayer(testApp.getPackage()).and().showsLayer(SCREENSHOT_LAYER) + .then() + .replaceVisibleLayer(SCREENSHOT_LAYER, testApp.getPackage()) + .forAllEntries() + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt new file mode 100644 index 000000000000..d53af6f53bf2 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.rotation + +import android.content.Intent +import android.view.Surface +import androidx.test.InstrumentationRegistry +import androidx.test.filters.FlakyTest +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.RotationTestBase +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WindowUtils +import com.android.server.wm.flicker.testapp.ActivityOptions +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Cycle through supported app rotations using seamless rotations. + * To run this test: `atest FlickerTests:SeamlessAppRotationTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 147659548) +@Ignore("Waiting bug feedback") +class SeamlessAppRotationTest( + private val intent: Intent, + beginRotationName: String, + endRotationName: String, + beginRotation: Int, + endRotation: Int +) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) { + override val transitionToRun: TransitionRunner + get() { + var intentId = "" + if (intent.extras?.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) { + intentId = "BUSY_UI_THREAD" + } + return CommonTransitions.changeAppRotation(intent, intentId, + InstrumentationRegistry.getContext(), instrumentation, uiDevice, + beginRotation, endRotation).build() + } + + @Test + fun checkPosition_appLayerRotates() { + val startingPos = WindowUtils.getAppPosition(beginRotation) + val endingPos = WindowUtils.getAppPosition(endRotation) + if (startingPos == endingPos) { + checkResults { + LayersTraceSubject.assertThat(it) + .hasVisibleRegion(intent.component?.packageName ?: "", startingPos) + .forAllEntries() + } + } else { + checkResults { + LayersTraceSubject.assertThat(it) + .hasVisibleRegion(intent.component?.packageName ?: "", startingPos) + .then() + .hasVisibleRegion(intent.component?.packageName ?: "", endingPos) + .forAllEntries() + } + } + } + + @Test + fun checkCoveredRegion_noUncoveredRegions() { + val startingBounds = WindowUtils.getDisplayBounds(beginRotation) + val endingBounds = WindowUtils.getDisplayBounds(endRotation) + if (startingBounds == endingBounds) { + checkResults { + LayersTraceSubject.assertThat(it) + .coversRegion(startingBounds) + .forAllEntries() + } + } else { + checkResults { + LayersTraceSubject.assertThat(it) + .coversRegion(startingBounds) + .then() + .coversRegion(endingBounds) + .forAllEntries() + } + } + } + + companion object { + // launch test activity that supports seamless rotation + + // launch test activity that supports seamless rotation with a busy UI thread to miss frames + // when the app is asked to redraw + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + val params: MutableCollection<Array<Any>> = ArrayList() + val testIntents = ArrayList<Intent>() + + // launch test activity that supports seamless rotation + var intent = Intent(Intent.ACTION_MAIN) + intent.component = ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME + testIntents.add(intent) + + // launch test activity that supports seamless rotation with a busy UI thread to miss frames + // when the app is asked to redraw + intent = Intent(intent) + intent.putExtra(ActivityOptions.EXTRA_STARVE_UI_THREAD, true) + testIntents.add(intent) + for (testIntent in testIntents) { + for (begin in supportedRotations) { + for (end in supportedRotations) { + if (begin != end) { + var testId: String = Surface.rotationToString(begin) + + "_" + Surface.rotationToString(end) + if (testIntent.extras?.getBoolean( + ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) { + testId += "_" + "BUSY_UI_THREAD" + } + params.add(arrayOf(testId, testIntent, begin, end)) + } + } + } + } + return params + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt new file mode 100644 index 000000000000..b5611a45a2e7 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.splitscreen + +import androidx.test.filters.LargeTest +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.NonRotationTestBase +import com.android.server.wm.flicker.StandardAppHelper +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WmTraceSubject +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest FlickerTests:OpenAppToSplitScreenTest` + */ +@LargeTest +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class OpenAppToSplitScreenTest( + beginRotationName: String, + beginRotation: Int +) : NonRotationTestBase(beginRotationName, beginRotation) { + init { + testApp = StandardAppHelper(instrumentation, + "com.android.server.wm.flicker.testapp", "SimpleApp") + } + + override val transitionToRun: TransitionRunner + get() = CommonTransitions.appToSplitScreen(testApp, instrumentation, uiDevice, + beginRotation).includeJankyRuns().build() + + @Test + fun checkVisibility_navBarWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries() + } + } + + @Test + fun checkVisibility_statusBarWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries() + } + } + + @Test + fun checkVisibility_dividerLayerBecomesVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .hidesLayer(DOCKED_STACK_DIVIDER) + .then() + .showsLayer(DOCKED_STACK_DIVIDER) + .forAllEntries() + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt new file mode 100644 index 000000000000..b6cce266ab88 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.splitscreen + +import android.graphics.Region +import android.util.Rational +import android.view.Surface +import androidx.test.InstrumentationRegistry +import androidx.test.filters.FlakyTest +import androidx.test.filters.LargeTest +import androidx.test.runner.AndroidJUnit4 +import androidx.test.uiautomator.UiDevice +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.FlickerTestBase +import com.android.server.wm.flicker.LayersTrace +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.StandardAppHelper +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.TransitionResult +import com.android.server.wm.flicker.WindowUtils +import com.android.server.wm.flicker.WmTraceSubject +import com.android.server.wm.flicker.helpers.AutomationUtils +import com.android.server.wm.flicker.helpers.ImeAppHelper +import com.google.common.truth.Truth +import org.junit.AfterClass +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters + +/** + * Test split screen resizing window transitions. + * To run this test: `atest FlickerTests:ResizeSplitScreenTest` + * + * Currently it runs only in 0 degrees because of b/156100803 + */ +@LargeTest +@RunWith(AndroidJUnit4::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 159096424) +@Ignore("Waiting bug feedback") +class ResizeSplitScreenTest : FlickerTestBase() { + init { + testApp = StandardAppHelper(instrumentation, + "com.android.server.wm.flicker.testapp", "SimpleApp") + } + + override val transitionToRun: TransitionRunner + get() { + val bottomApp = ImeAppHelper(instrumentation) + return CommonTransitions.resizeSplitScreen(testApp, bottomApp, instrumentation, + uiDevice, Surface.ROTATION_0, + Rational(1, 3), Rational(2, 3)) + .includeJankyRuns().build() + } + + @Test + fun checkVisibility_topAppLayerIsAlwaysVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(sSimpleActivity) + .forAllEntries() + } + } + + @Test + fun checkVisibility_bottomAppLayerIsAlwaysVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(sImeActivity) + .forAllEntries() + } + } + + @Test + fun checkVisibility_dividerLayerIsAlwaysVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(DOCKED_STACK_DIVIDER) + .forAllEntries() + } + } + + @Test + @Ignore("Waiting feedback") + fun checkPosition_appsStartingBounds() { + val displayBounds = WindowUtils.getDisplayBounds() + checkResults { result: TransitionResult -> + val entries = LayersTrace.parseFrom(result.layersTrace, + result.layersTracePath, result.layersTraceChecksum) + Truth.assertThat(entries.entries).isNotEmpty() + val startingDividerBounds = entries.entries[0].getVisibleBounds( + DOCKED_STACK_DIVIDER).bounds + val startingTopAppBounds = Region(0, 0, startingDividerBounds.right, + startingDividerBounds.top + WindowUtils.getDockedStackDividerInset()) + val startingBottomAppBounds = Region(0, + startingDividerBounds.bottom - WindowUtils.getDockedStackDividerInset(), + displayBounds.right, + displayBounds.bottom - WindowUtils.getNavigationBarHeight()) + LayersTraceSubject.assertThat(result) + .hasVisibleRegion("SimpleActivity", startingTopAppBounds) + .inTheBeginning() + LayersTraceSubject.assertThat(result) + .hasVisibleRegion("ImeActivity", startingBottomAppBounds) + .inTheBeginning() + } + } + + @Test + @Ignore("Waiting feedback") + fun checkPosition_appsEndingBounds() { + val displayBounds = WindowUtils.getDisplayBounds() + checkResults { result: TransitionResult -> + val entries = LayersTrace.parseFrom(result.layersTrace, + result.layersTracePath, result.layersTraceChecksum) + Truth.assertThat(entries.entries).isNotEmpty() + val endingDividerBounds = entries.entries[entries.entries.size - 1].getVisibleBounds( + DOCKED_STACK_DIVIDER).bounds + val startingTopAppBounds = Region(0, 0, endingDividerBounds.right, + endingDividerBounds.top + WindowUtils.getDockedStackDividerInset()) + val startingBottomAppBounds = Region(0, + endingDividerBounds.bottom - WindowUtils.getDockedStackDividerInset(), + displayBounds.right, + displayBounds.bottom - WindowUtils.getNavigationBarHeight()) + LayersTraceSubject.assertThat(result) + .hasVisibleRegion(sSimpleActivity, startingTopAppBounds) + .atTheEnd() + LayersTraceSubject.assertThat(result) + .hasVisibleRegion(sImeActivity, startingBottomAppBounds) + .atTheEnd() + } + } + + @Test + fun checkVisibility_navBarWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE) + .forAllEntries() + } + } + + @Test + fun checkVisibility_statusBarWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE) + .forAllEntries() + } + } + + @Test + @FlakyTest(bugId = 156223549) + @Ignore("Waiting bug feedback") + fun checkVisibility_topAppWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAppWindow(sSimpleActivity) + .forAllEntries() + } + } + + @Test + @FlakyTest(bugId = 156223549) + @Ignore("Waiting bug feedback") + fun checkVisibility_bottomAppWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAppWindow(sImeActivity) + .forAllEntries() + } + } + + companion object { + private const val sSimpleActivity = "SimpleActivity" + private const val sImeActivity = "ImeActivity" + + @AfterClass + @JvmStatic + fun teardown() { + val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + if (AutomationUtils.isInSplitScreen(device)) { + AutomationUtils.exitSplitScreen(device) + } + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt new file mode 100644 index 000000000000..fdcafdb12a78 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.splitscreen + +import android.view.Surface +import androidx.test.InstrumentationRegistry +import androidx.test.filters.LargeTest +import androidx.test.runner.AndroidJUnit4 +import androidx.test.uiautomator.UiDevice +import com.android.server.wm.flicker.CommonTransitions +import com.android.server.wm.flicker.FlickerTestBase +import com.android.server.wm.flicker.LayersTraceSubject +import com.android.server.wm.flicker.StandardAppHelper +import com.android.server.wm.flicker.TransitionRunner +import com.android.server.wm.flicker.WindowUtils +import com.android.server.wm.flicker.WmTraceSubject +import com.android.server.wm.flicker.helpers.AutomationUtils +import org.junit.AfterClass +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters + +/** + * Test open app to split screen. + * To run this test: `atest FlickerTests:SplitScreenToLauncherTest` + */ +@LargeTest +@RunWith(AndroidJUnit4::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class SplitScreenToLauncherTest : FlickerTestBase() { + init { + testApp = StandardAppHelper(InstrumentationRegistry.getInstrumentation(), + "com.android.server.wm.flicker.testapp", "SimpleApp") + } + + override val transitionToRun: TransitionRunner + get() = CommonTransitions.splitScreenToLauncher(testApp, instrumentation, uiDevice, + Surface.ROTATION_0).includeJankyRuns().build() + + @Test + fun checkCoveredRegion_noUncoveredRegions() { + checkResults { + LayersTraceSubject.assertThat(it) + .coversRegion(WindowUtils.getDisplayBounds()).forAllEntries() + } + } + + @Test + fun checkVisibility_dividerLayerBecomesInVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(DOCKED_STACK_DIVIDER) + .then() + .hidesLayer(DOCKED_STACK_DIVIDER) + .forAllEntries() + } + } + + @Test + fun checkVisibility_appLayerBecomesInVisible() { + checkResults { + LayersTraceSubject.assertThat(it) + .showsLayer(testApp.getPackage()) + .then() + .hidesLayer(testApp.getPackage()) + .forAllEntries() + } + } + + @Test + fun checkVisibility_navBarWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries() + } + } + + @Test + fun checkVisibility_statusBarWindowIsAlwaysVisible() { + checkResults { + WmTraceSubject.assertThat(it) + .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries() + } + } + + companion object { + @AfterClass + @JvmStatic + fun teardown() { + val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + if (AutomationUtils.isInSplitScreen(device)) { + AutomationUtils.exitSplitScreen(device) + } + } + } +} diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 55bbe22c4d86..c2fd0c39221e 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -453,6 +453,32 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { after.forEach(dir -> assertDirectoryIsEmpty(dir)); } + @Test + public void testExpireApexRollback() throws Exception { + List<String> before = getSnapshotDirectories("/data/misc_ce/0/apexrollback"); + pushTestApex(); + + // Push files to apex data directory + String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1; + String oldFilePath2 = + apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2; + assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1)); + assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2)); + + // Install new version of the APEX with rollback enabled + runPhase("testRollbackApexDataDirectories_Phase1"); + getDevice().reboot(); + + List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback"); + // Only check directories newly created during the test + after.removeAll(before); + // Expire all rollbacks and check CE snapshot directories are deleted + runPhase("testCleanUp"); + for (String dir : after) { + assertNull(getDevice().getFileEntry(dir)); + } + } + private void pushTestApex() throws Exception { CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"; diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index a230de46dcf3..4c741c49cfdb 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -25,6 +25,7 @@ namespace android { namespace stats_log_api_gen { +using google::protobuf::OneofDescriptor; using google::protobuf::EnumDescriptor; using google::protobuf::FieldDescriptor; using google::protobuf::FileDescriptor; @@ -396,16 +397,14 @@ int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t> collate_enums(*field->enum_type(), &atField); } - // Generate signature for pushed atoms - if (atomDecl->code < PULL_ATOM_START_ID) { - if (javaType == JAVA_TYPE_ENUM) { - // All enums are treated as ints when it comes to function signatures. - signature->push_back(JAVA_TYPE_INT); - } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) { - signature->push_back(JAVA_TYPE_BYTE_ARRAY); - } else { - signature->push_back(javaType); - } + // Generate signature for atom. + if (javaType == JAVA_TYPE_ENUM) { + // All enums are treated as ints when it comes to function signatures. + signature->push_back(JAVA_TYPE_INT); + } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) { + signature->push_back(JAVA_TYPE_BYTE_ARRAY); + } else { + signature->push_back(javaType); } atomDecl->fields.push_back(atField); @@ -518,8 +517,7 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* shared_ptr<AtomDecl> atomDecl = make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name()); - if (atomDecl->code < PULL_ATOM_START_ID && - atomField->options().GetExtension(os::statsd::truncate_timestamp)) { + if (atomField->options().GetExtension(os::statsd::truncate_timestamp)) { addAnnotationToAtomDecl(atomDecl.get(), ATOM_ID_FIELD_NUMBER, ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL, AnnotationValue(true)); @@ -537,7 +535,24 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* continue; } - FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = atoms->signatureInfoMap[signature]; + const OneofDescriptor* oneofAtom = atomField->containing_oneof(); + if (oneofAtom == nullptr) { + print_error(atomField, "Atom is not declared in a `oneof` field: %s\n", + atomField->name().c_str()); + errorCount++; + continue; + } + else if ((oneofAtom->name() != ONEOF_PUSHED_ATOM_NAME) && + (oneofAtom->name() != ONEOF_PULLED_ATOM_NAME)) { + print_error(atomField, "Atom is neither a pushed nor pulled atom: %s\n", + atomField->name().c_str()); + errorCount++; + continue; + } + + FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = oneofAtom->name() == + ONEOF_PUSHED_ATOM_NAME ? atoms->signatureInfoMap[signature] : + atoms->pulledAtomsSignatureInfoMap[signature]; populateFieldNumberToAtomDeclSet(atomDecl, &fieldNumberToAtomDeclSet); atoms->decls.insert(atomDecl); @@ -556,6 +571,7 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* } if (dbg) { + // Signatures for pushed atoms. printf("signatures = [\n"); for (SignatureInfoMap::const_iterator it = atoms->signatureInfoMap.begin(); it != atoms->signatureInfoMap.end(); it++) { @@ -566,6 +582,17 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* } printf("\n"); } + + // Signatures for pull atoms. + for (SignatureInfoMap::const_iterator it = atoms->pulledAtomsSignatureInfoMap.begin(); + it != atoms->pulledAtomsSignatureInfoMap.end(); it++) { + printf(" "); + for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end(); + jt++) { + printf(" %d", (int)*jt); + } + printf("\n"); + } printf("]\n"); } diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index 10b34ecf5f54..e637ed945a08 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -29,6 +29,7 @@ namespace android { namespace stats_log_api_gen { +using google::protobuf::OneofDescriptor; using google::protobuf::Descriptor; using google::protobuf::FieldDescriptor; using std::map; @@ -41,6 +42,14 @@ const int PULL_ATOM_START_ID = 10000; const int FIRST_UID_IN_CHAIN_ID = 0; +/** + * The types of oneof atoms. + * + * `OneofDescriptor::name()` returns the name of the oneof. + */ +const string ONEOF_PUSHED_ATOM_NAME = "pushed"; +const string ONEOF_PULLED_ATOM_NAME = "pulled"; + enum AnnotationId : uint8_t { ANNOTATION_ID_IS_UID = 1, ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2, @@ -184,6 +193,7 @@ using SignatureInfoMap = map<vector<java_type_t>, FieldNumberToAtomDeclSet>; struct Atoms { SignatureInfoMap signatureInfoMap; + SignatureInfoMap pulledAtomsSignatureInfoMap; AtomDeclSet decls; AtomDeclSet non_chained_decls; SignatureInfoMap nonChainedSignatureInfoMap; diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp index f4c937c3f599..ffbe9f800736 100644 --- a/tools/stats_log_api_gen/java_writer.cpp +++ b/tools/stats_log_api_gen/java_writer.cpp @@ -96,29 +96,160 @@ static void write_annotations(FILE* out, int argIndex, } } +static void write_method_signature(FILE* out, const vector<java_type_t>& signature, + const AtomDecl& attributionDecl) { + int argIndex = 1; + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { + if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { + for (auto chainField : attributionDecl.fields) { + fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), + chainField.name.c_str()); + } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", android.util.SparseArray<Object> valueMap"); + } else { + fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex); + } + argIndex++; + } +} + +static int write_method_body(FILE* out, const vector<java_type_t>& signature, + const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet, + const AtomDecl& attributionDecl, const string& indent) { + // Start StatsEvent.Builder. + fprintf(out, + "%s final StatsEvent.Builder builder = " + "StatsEvent.newBuilder();\n", + indent.c_str()); + + // Write atom code. + fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str()); + write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet); + + // Write the args. + int argIndex = 1; + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { + switch (*arg) { + case JAVA_TYPE_BOOLEAN: + fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(), + argIndex); + break; + case JAVA_TYPE_INT: + case JAVA_TYPE_ENUM: + fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex); + break; + case JAVA_TYPE_FLOAT: + fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(), + argIndex); + break; + case JAVA_TYPE_LONG: + fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex); + break; + case JAVA_TYPE_STRING: + fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(), + argIndex); + break; + case JAVA_TYPE_BYTE_ARRAY: + fprintf(out, + "%s builder.writeByteArray(null == arg%d ? new byte[0] : " + "arg%d);\n", + indent.c_str(), argIndex, argIndex); + break; + case JAVA_TYPE_ATTRIBUTION_CHAIN: { + const char* uidName = attributionDecl.fields.front().name.c_str(); + const char* tagName = attributionDecl.fields.back().name.c_str(); + + fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str()); + fprintf(out, "%s null == %s ? new int[0] : %s,\n", + indent.c_str(), uidName, uidName); + fprintf(out, "%s null == %s ? new String[0] : %s);\n", + indent.c_str(), tagName, tagName); + break; + } + case JAVA_TYPE_KEY_VALUE_PAIR: + fprintf(out, "\n"); + fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str()); + fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str()); + fprintf(out, "%s android.util.SparseIntArray intMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseLongArray longMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n", + indent.c_str()); + fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str()); + fprintf(out, "%s final int key = valueMap.keyAt(i);\n", + indent.c_str()); + fprintf(out, "%s final Object value = valueMap.valueAt(i);\n", + indent.c_str()); + fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str()); + fprintf(out, "%s if (null == intMap) {\n", indent.c_str()); + fprintf(out, + "%s intMap = new " + "android.util.SparseIntArray();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s intMap.put(key, (Integer) value);\n", + indent.c_str()); + fprintf(out, "%s } else if (value instanceof Long) {\n", + indent.c_str()); + fprintf(out, "%s if (null == longMap) {\n", indent.c_str()); + fprintf(out, + "%s longMap = new " + "android.util.SparseLongArray();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s longMap.put(key, (Long) value);\n", + indent.c_str()); + fprintf(out, "%s } else if (value instanceof String) {\n", + indent.c_str()); + fprintf(out, "%s if (null == stringMap) {\n", indent.c_str()); + fprintf(out, + "%s stringMap = new " + "android.util.SparseArray<>();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s stringMap.put(key, (String) value);\n", + indent.c_str()); + fprintf(out, "%s } else if (value instanceof Float) {\n", + indent.c_str()); + fprintf(out, "%s if (null == floatMap) {\n", indent.c_str()); + fprintf(out, + "%s floatMap = new " + "android.util.SparseArray<>();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s floatMap.put(key, (Float) value);\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, + "%s builder.writeKeyValuePairs(" + "intMap, longMap, stringMap, floatMap);\n", + indent.c_str()); + break; + default: + // Unsupported types: OBJECT, DOUBLE. + fprintf(stderr, "Encountered unsupported type."); + return 1; + } + write_annotations(out, argIndex, fieldNumberToAtomDeclSet); + argIndex++; + } + return 0; +} + static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMap, const AtomDecl& attributionDecl, const bool supportQ) { for (auto signatureInfoMapIt = signatureInfoMap.begin(); signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { // Print method signature. fprintf(out, " public static void write(int code"); - const vector<java_type_t>& signature = signatureInfoMapIt->first; - const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second; - int argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); - arg++) { - if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { - for (auto chainField : attributionDecl.fields) { - fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), - chainField.name.c_str()); - } - } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", android.util.SparseArray<Object> valueMap"); - } else { - fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex); - } - argIndex++; - } + write_method_signature(out, signatureInfoMapIt->first, attributionDecl); fprintf(out, ") {\n"); // Print method body. @@ -128,130 +259,13 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa indent = " "; } - // Start StatsEvent.Builder. - fprintf(out, - "%s final StatsEvent.Builder builder = " - "StatsEvent.newBuilder();\n", - indent.c_str()); - - // Write atom code. - fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str()); - write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet); - - // Write the args. - argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); - arg++) { - switch (*arg) { - case JAVA_TYPE_BOOLEAN: - fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(), - argIndex); - break; - case JAVA_TYPE_INT: - case JAVA_TYPE_ENUM: - fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex); - break; - case JAVA_TYPE_FLOAT: - fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(), - argIndex); - break; - case JAVA_TYPE_LONG: - fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex); - break; - case JAVA_TYPE_STRING: - fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(), - argIndex); - break; - case JAVA_TYPE_BYTE_ARRAY: - fprintf(out, - "%s builder.writeByteArray(null == arg%d ? new byte[0] : " - "arg%d);\n", - indent.c_str(), argIndex, argIndex); - break; - case JAVA_TYPE_ATTRIBUTION_CHAIN: { - const char* uidName = attributionDecl.fields.front().name.c_str(); - const char* tagName = attributionDecl.fields.back().name.c_str(); - - fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str()); - fprintf(out, "%s null == %s ? new int[0] : %s,\n", - indent.c_str(), uidName, uidName); - fprintf(out, "%s null == %s ? new String[0] : %s);\n", - indent.c_str(), tagName, tagName); - break; - } - case JAVA_TYPE_KEY_VALUE_PAIR: - fprintf(out, "\n"); - fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str()); - fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str()); - fprintf(out, "%s android.util.SparseIntArray intMap = null;\n", - indent.c_str()); - fprintf(out, "%s android.util.SparseLongArray longMap = null;\n", - indent.c_str()); - fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n", - indent.c_str()); - fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n", - indent.c_str()); - fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str()); - fprintf(out, "%s final int key = valueMap.keyAt(i);\n", - indent.c_str()); - fprintf(out, "%s final Object value = valueMap.valueAt(i);\n", - indent.c_str()); - fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str()); - fprintf(out, "%s if (null == intMap) {\n", indent.c_str()); - fprintf(out, - "%s intMap = new " - "android.util.SparseIntArray();\n", - indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s intMap.put(key, (Integer) value);\n", - indent.c_str()); - fprintf(out, "%s } else if (value instanceof Long) {\n", - indent.c_str()); - fprintf(out, "%s if (null == longMap) {\n", indent.c_str()); - fprintf(out, - "%s longMap = new " - "android.util.SparseLongArray();\n", - indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s longMap.put(key, (Long) value);\n", - indent.c_str()); - fprintf(out, "%s } else if (value instanceof String) {\n", - indent.c_str()); - fprintf(out, "%s if (null == stringMap) {\n", indent.c_str()); - fprintf(out, - "%s stringMap = new " - "android.util.SparseArray<>();\n", - indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s stringMap.put(key, (String) value);\n", - indent.c_str()); - fprintf(out, "%s } else if (value instanceof Float) {\n", - indent.c_str()); - fprintf(out, "%s if (null == floatMap) {\n", indent.c_str()); - fprintf(out, - "%s floatMap = new " - "android.util.SparseArray<>();\n", - indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s floatMap.put(key, (Float) value);\n", - indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, - "%s builder.writeKeyValuePairs(" - "intMap, longMap, stringMap, floatMap);\n", - indent.c_str()); - break; - default: - // Unsupported types: OBJECT, DOUBLE. - fprintf(stderr, "Encountered unsupported type."); - return 1; - } - write_annotations(out, argIndex, fieldNumberToAtomDeclSet); - argIndex++; + int ret = write_method_body(out, signatureInfoMapIt->first, signatureInfoMapIt->second, + attributionDecl, indent); + if (ret != 0) { + return ret; } - fprintf(out, "\n"); + fprintf(out, "%s builder.usePooledBuffer();\n", indent.c_str()); fprintf(out, "%s StatsLog.write(builder.build());\n", indent.c_str()); @@ -259,9 +273,9 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa if (supportQ) { fprintf(out, " } else {\n"); fprintf(out, " QLogger.write(code"); - argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + int argIndex = 1; + for (vector<java_type_t>::const_iterator arg = signatureInfoMapIt->first.begin(); + arg != signatureInfoMapIt->first.end(); arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { const char* uidName = attributionDecl.fields.front().name.c_str(); const char* tagName = attributionDecl.fields.back().name.c_str(); @@ -285,6 +299,32 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa return 0; } +static int write_java_build_stats_event_methods(FILE* out, const SignatureInfoMap& signatureInfoMap, + const AtomDecl& attributionDecl) { + for (auto signatureInfoMapIt = signatureInfoMap.begin(); + signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { + // Print method signature. + fprintf(out, " public static StatsEvent buildStatsEvent(int code"); + write_method_signature(out, signatureInfoMapIt->first, attributionDecl); + fprintf(out, ") {\n"); + + // Print method body. + string indent(""); + int ret = write_method_body(out, signatureInfoMapIt->first, signatureInfoMapIt->second, + attributionDecl, indent); + if (ret != 0) { + return ret; + } + fprintf(out, "\n"); + + fprintf(out, "%s return builder.build();\n", indent.c_str()); + + fprintf(out, " }\n"); // method + fprintf(out, "\n"); + } + return 0; +} + int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl, const string& javaClass, const string& javaPackage, const bool supportQ, const bool supportWorkSource) { @@ -319,6 +359,8 @@ int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attribut fprintf(out, " // Write methods\n"); errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ); errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap); + errors += write_java_build_stats_event_methods(out, atoms.pulledAtomsSignatureInfoMap, + attributionDecl); if (supportWorkSource) { errors += write_java_work_source_methods(out, atoms.signatureInfoMap); } diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto index aaa488e44fee..e658b62b8daa 100644 --- a/tools/stats_log_api_gen/test.proto +++ b/tools/stats_log_api_gen/test.proto @@ -58,7 +58,7 @@ message AllTypesAtom { } message Event { - oneof event { + oneof pushed { OutOfOrderAtom out_of_order_atom = 2; IntAtom int_atom = 1; AnotherIntAtom another_int_atom = 3; @@ -74,7 +74,7 @@ message BadTypesAtom { } message BadTypesEvent { - oneof event { + oneof pushed { BadTypesAtom bad_types_atom = 1; } } @@ -84,7 +84,7 @@ message BadSkippedFieldSingleAtom { } message BadSkippedFieldSingle { - oneof event { + oneof pushed { BadSkippedFieldSingleAtom bad = 1; } } @@ -96,7 +96,7 @@ message BadSkippedFieldMultipleAtom { } message BadSkippedFieldMultiple { - oneof event { + oneof pushed { BadSkippedFieldMultipleAtom bad = 1; } } @@ -107,11 +107,11 @@ message BadAttributionNodePositionAtom { } message BadAttributionNodePosition { - oneof event { BadAttributionNodePositionAtom bad = 1; } + oneof pushed { BadAttributionNodePositionAtom bad = 1; } } message GoodEventWithBinaryFieldAtom { - oneof event { GoodBinaryFieldAtom field1 = 1; } + oneof pushed { GoodBinaryFieldAtom field1 = 1; } } message ComplexField { @@ -124,7 +124,7 @@ message GoodBinaryFieldAtom { } message BadEventWithBinaryFieldAtom { - oneof event { BadBinaryFieldAtom field1 = 1; } + oneof pushed { BadBinaryFieldAtom field1 = 1; } } message BadBinaryFieldAtom { @@ -133,7 +133,7 @@ message BadBinaryFieldAtom { } message BadStateAtoms { - oneof event { + oneof pushed { BadStateAtom1 bad1 = 1; BadStateAtom2 bad2 = 2; BadStateAtom3 bad3 = 3; @@ -141,7 +141,7 @@ message BadStateAtoms { } message GoodStateAtoms { - oneof event { + oneof pushed { GoodStateAtom1 good1 = 1; GoodStateAtom2 good2 = 2; } @@ -204,7 +204,7 @@ message NoModuleAtom { } message ModuleAtoms { - oneof event { + oneof pushed { ModuleOneAtom module_one_atom = 1 [(android.os.statsd.module) = "module1"]; ModuleTwoAtom module_two_atom = 2 [(android.os.statsd.module) = "module2"]; ModuleOneAndTwoAtom module_one_and_two_atom = 3 [ @@ -213,3 +213,24 @@ message ModuleAtoms { NoModuleAtom no_module_atom = 4; } } + +message NotAPushNorPullAtom { + oneof event { + IntAtom int_atom = 1; + } +} + +message AtomNotInAOneof { + optional IntAtom int_atom = 1; +} + +message PushedAndPulledAtoms { + oneof pushed { + IntAtom int_atom_1 = 1; + } + + oneof pulled { + OutOfOrderAtom out_of_order_atom = 11; + AnotherIntAtom another_int_atom = 10; + } +} diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp index dbae58889333..5fd728a29c07 100644 --- a/tools/stats_log_api_gen/test_collation.cpp +++ b/tools/stats_log_api_gen/test_collation.cpp @@ -365,5 +365,69 @@ TEST(CollationTest, RecognizeModule1Atom) { EXPECT_TRUE(annotation->value.boolValue); } +/** + * Test that an atom is not a pushed nor pulled atom. + */ +TEST(CollationTest, InvalidAtomType) { + Atoms atoms; + int errorCount = collate_atoms(NotAPushNorPullAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms); + + EXPECT_EQ(1, errorCount); +} + +/** + * Test that an atom was not declared in a `oneof` field. + */ +TEST(CollationTest, AtomNotDeclaredInAOneof) { + Atoms atoms; + int errorCount = collate_atoms(AtomNotInAOneof::descriptor(), DEFAULT_MODULE_NAME, &atoms); + + EXPECT_EQ(1, errorCount); +} + +/** + * Test a correct collation with pushed and pulled atoms. + */ +TEST(CollationTest, CollatePushedAndPulledAtoms) { + Atoms atoms; + int errorCount = collate_atoms(PushedAndPulledAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms); + + EXPECT_EQ(0, errorCount); + EXPECT_EQ(1ul, atoms.signatureInfoMap.size()); + EXPECT_EQ(2ul, atoms.pulledAtomsSignatureInfoMap.size()); + + // IntAtom + EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT); + + // AnotherIntAtom + EXPECT_MAP_CONTAINS_SIGNATURE(atoms.pulledAtomsSignatureInfoMap, JAVA_TYPE_INT); + + // OutOfOrderAtom + EXPECT_MAP_CONTAINS_SIGNATURE(atoms.pulledAtomsSignatureInfoMap, JAVA_TYPE_INT, JAVA_TYPE_INT); + + EXPECT_EQ(3ul, atoms.decls.size()); + + AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); + EXPECT_EQ(1, (*atomIt)->code); + EXPECT_EQ("int_atom_1", (*atomIt)->name); + EXPECT_EQ("IntAtom", (*atomIt)->message); + EXPECT_NO_ENUM_FIELD((*atomIt)); + atomIt++; + + EXPECT_EQ(10, (*atomIt)->code); + EXPECT_EQ("another_int_atom", (*atomIt)->name); + EXPECT_EQ("AnotherIntAtom", (*atomIt)->message); + EXPECT_NO_ENUM_FIELD((*atomIt)); + atomIt++; + + EXPECT_EQ(11, (*atomIt)->code); + EXPECT_EQ("out_of_order_atom", (*atomIt)->name); + EXPECT_EQ("OutOfOrderAtom", (*atomIt)->message); + EXPECT_NO_ENUM_FIELD((*atomIt)); + atomIt++; + + EXPECT_EQ(atoms.decls.end(), atomIt); +} + } // namespace stats_log_api_gen } // namespace android |