diff options
87 files changed, 4155 insertions, 271 deletions
diff --git a/api/current.txt b/api/current.txt index 7c32ef04c6f9..1b5b3ee26a9f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -480,6 +480,10 @@ package android { field public static final int dashGap = 16843175; // 0x10101a7 field public static final int dashWidth = 16843174; // 0x10101a6 field public static final int data = 16842798; // 0x101002e + field public static final int dataRetentionTime = 16844189; // 0x101059d + field public static final int dataSentOffDevice = 16844186; // 0x101059a + field public static final int dataSharedWithThirdParty = 16844187; // 0x101059b + field public static final int dataUsedForMonetization = 16844188; // 0x101059c field public static final int datePickerDialogTheme = 16843948; // 0x10104ac field public static final int datePickerMode = 16843955; // 0x10104b3 field public static final int datePickerStyle = 16843612; // 0x101035c @@ -1495,6 +1499,7 @@ package android { field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344 field public static final int unselectedAlpha = 16843278; // 0x101020e field public static final int updatePeriodMillis = 16843344; // 0x1010250 + field public static final int usageInfoRequired = 16844185; // 0x1010599 field public static final int use32bitAbi = 16844053; // 0x1010515 field public static final int useAppZygote = 16844184; // 0x1010598 field public static final int useDefaultMargins = 16843641; // 0x1010379 @@ -3802,6 +3807,7 @@ package android.app { method public void overridePendingTransition(int, int); method public void postponeEnterTransition(); method public void recreate(); + method public void registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks); method public void registerForContextMenu(android.view.View); method public boolean releaseInstance(); method public final deprecated void removeDialog(int); @@ -3879,6 +3885,7 @@ package android.app { method public deprecated void stopManagingCursor(android.database.Cursor); method public void takeKeyEvents(boolean); method public void triggerSearch(java.lang.String, android.os.Bundle); + method public void unregisterActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks); method public void unregisterForContextMenu(android.view.View); field public static final int DEFAULT_KEYS_DIALER = 1; // 0x1 field public static final int DEFAULT_KEYS_DISABLE = 0; // 0x0 @@ -11169,8 +11176,8 @@ package android.content.pm { field public android.content.pm.ProviderInfo[] providers; field public android.content.pm.ActivityInfo[] receivers; field public android.content.pm.FeatureInfo[] reqFeatures; - field public java.lang.String[] requestedPermissions; - field public int[] requestedPermissionsFlags; + field public deprecated java.lang.String[] requestedPermissions; + field public deprecated int[] requestedPermissionsFlags; field public android.content.pm.ServiceInfo[] services; field public java.lang.String sharedUserId; field public int sharedUserLabel; @@ -11178,6 +11185,7 @@ package android.content.pm { field public android.content.pm.SigningInfo signingInfo; field public java.lang.String[] splitNames; field public int[] splitRevisionCodes; + field public android.content.pm.UsesPermissionInfo[] usesPermissions; field public deprecated int versionCode; field public java.lang.String versionName; } @@ -11653,6 +11661,7 @@ package android.content.pm { field public java.lang.String group; field public java.lang.CharSequence nonLocalizedDescription; field public deprecated int protectionLevel; + field public boolean usageInfoRequired; } public final class ProviderInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable { @@ -11832,6 +11841,28 @@ package android.content.pm { field public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR; } + public final class UsesPermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + method public int describeContents(); + method public int getDataRetention(); + method public int getDataRetentionWeeks(); + method public int getDataSentOffDevice(); + method public int getDataSharedWithThirdParty(); + method public int getDataUsedForMonetization(); + method public int getFlags(); + method public java.lang.String getPermission(); + field public static final android.os.Parcelable.Creator<android.content.pm.UsesPermissionInfo> CREATOR; + field public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 2; // 0x2 + field public static final int RETENTION_NOT_RETAINED = 1; // 0x1 + field public static final int RETENTION_SPECIFIED = 4; // 0x4 + field public static final int RETENTION_UNDEFINED = 0; // 0x0 + field public static final int RETENTION_UNLIMITED = 3; // 0x3 + field public static final int RETENTION_USER_SELECTED = 2; // 0x2 + field public static final int USAGE_NO = 3; // 0x3 + field public static final int USAGE_UNDEFINED = 0; // 0x0 + field public static final int USAGE_USER_TRIGGERED = 2; // 0x2 + field public static final int USAGE_YES = 1; // 0x1 + } + public final class VersionedPackage implements android.os.Parcelable { ctor public VersionedPackage(java.lang.String, int); ctor public VersionedPackage(java.lang.String, long); @@ -13941,6 +13972,7 @@ package android.graphics { } public final class Insets { + method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets); method public static android.graphics.Insets of(int, int, int, int); method public static android.graphics.Insets of(android.graphics.Rect); field public static final android.graphics.Insets NONE; @@ -14693,6 +14725,7 @@ package android.graphics { method public float getTranslationX(); method public float getTranslationY(); method public float getTranslationZ(); + method public long getUniqueId(); method public int getWidth(); method public boolean hasDisplayList(); method public boolean hasIdentityMatrix(); @@ -15046,6 +15079,7 @@ package android.graphics.drawable { method public void invalidateSelf(); method public boolean isAutoMirrored(); method public boolean isFilterBitmap(); + method public boolean isProjected(); method public boolean isStateful(); method public final boolean isVisible(); method public void jumpToCurrentState(); diff --git a/api/system-current.txt b/api/system-current.txt index b43f6c6ef040..b4c73362d7cc 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3685,6 +3685,10 @@ package android.net.wifi { public class WifiManager { method public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener); + method public void connect(int, android.net.wifi.WifiManager.ActionListener); + method public void disable(int, android.net.wifi.WifiManager.ActionListener); + method public void disableEphemeralNetwork(java.lang.String); + method public void forget(int, android.net.wifi.WifiManager.ActionListener); method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>); method public java.util.List<android.net.wifi.hotspot2.OsuProvider> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>); method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks(); @@ -3696,6 +3700,7 @@ package android.net.wifi { method public boolean isWifiApEnabled(); method public boolean isWifiScannerSupported(); method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler); + method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener); method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); method public boolean startScan(android.os.WorkSource); method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback); diff --git a/api/test-current.txt b/api/test-current.txt index 8c710c1237c6..c0f7ab63003b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -61,6 +61,7 @@ package android.app { } public class ActivityTaskManager { + method public void clearLaunchParamsForPackages(java.util.List<java.lang.String>); method public java.lang.String listAllStacks(); method public void moveTaskToStack(int, int, boolean); method public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect); diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 0277b7e3e71a..25bd0330ad5c 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -1461,7 +1461,6 @@ Landroid/view/IWindowManager;->setShelfHeight(ZI)V Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V Landroid/view/IWindowManager;->showStrictModeViolation(Z)V Landroid/view/IWindowManager;->thawRotation()V -Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I Landroid/view/IWindowSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowSession; Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V Landroid/view/IWindowSession;->getInTouchMode()Z diff --git a/config/preloaded-classes b/config/preloaded-classes index 5940c45466fb..30959256c922 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -4117,6 +4117,10 @@ com.android.internal.util.StateMachine$SmHandler$StateInfo com.android.internal.util.VirtualRefBasePtr com.android.internal.util.XmlUtils com.android.internal.util.XmlUtils$WriteMapCallback +com.android.internal.util.function.NonaConsumer +com.android.internal.util.function.NonaFunction +com.android.internal.util.function.OctConsumer +com.android.internal.util.function.OctFunction com.android.internal.util.function.HeptConsumer com.android.internal.util.function.HeptFunction com.android.internal.util.function.HexConsumer diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 83fab7e5a45d..05bb9a1c2139 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -827,6 +827,8 @@ public class Activity extends ContextThemeWrapper /** The screen observation manager. Always access via {@link #getIntelligenceManager()}. */ @Nullable private IntelligenceManager mIntelligenceManager; + private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks = + new ArrayList<Application.ActivityLifecycleCallbacks>(); static final class NonConfigurationInstances { Object activity; @@ -1065,6 +1067,288 @@ public class Activity extends ContextThemeWrapper } /** + * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives + * lifecycle callbacks for only this Activity. + * <p> + * In relation to any + * {@link Application#registerActivityLifecycleCallbacks Application registered callbacks}, + * the callbacks registered here will always occur nested within those callbacks. This means: + * <ul> + * <li>Pre events will first be sent to Application registered callbacks, then to callbacks + * registered here.</li> + * <li>{@link Application.ActivityLifecycleCallbacks#onActivityCreated(Activity, Bundle)}, + * {@link Application.ActivityLifecycleCallbacks#onActivityStarted(Activity)}, and + * {@link Application.ActivityLifecycleCallbacks#onActivityResumed(Activity)} will + * be sent first to Application registered callbacks, then to callbacks registered here. + * For all other events, callbacks registered here will be sent first.</li> + * <li>Post events will first be sent to callbacks registered here, then to + * Application registered callbacks.</li> + * </ul> + * <p> + * If multiple callbacks are registered here, they receive events in a first in (up through + * {@link Application.ActivityLifecycleCallbacks#onActivityPostResumed}, last out + * ordering. + * <p> + * It is strongly recommended to register this in the constructor of your Activity to ensure + * you get all available callbacks. As this callback is associated with only this Activity, + * it is not usually necessary to {@link #unregisterActivityLifecycleCallbacks unregister} it + * unless you specifically do not want to receive further lifecycle callbacks. + * + * @param callback The callback instance to register + */ + public void registerActivityLifecycleCallbacks( + @NonNull Application.ActivityLifecycleCallbacks callback) { + synchronized (mActivityLifecycleCallbacks) { + mActivityLifecycleCallbacks.add(callback); + } + } + + /** + * Unregister an {@link Application.ActivityLifecycleCallbacks} previously registered + * with {@link #registerActivityLifecycleCallbacks}. It will not receive any further + * callbacks. + * + * @param callback The callback instance to unregister + * @see #registerActivityLifecycleCallbacks + */ + public void unregisterActivityLifecycleCallbacks( + @NonNull Application.ActivityLifecycleCallbacks callback) { + synchronized (mActivityLifecycleCallbacks) { + mActivityLifecycleCallbacks.remove(callback); + } + } + + private void dispatchActivityPreCreated(@Nullable Bundle savedInstanceState) { + getApplication().dispatchActivityPreCreated(this, savedInstanceState); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreCreated(this, + savedInstanceState); + } + } + } + + private void dispatchActivityCreated(@Nullable Bundle savedInstanceState) { + getApplication().dispatchActivityCreated(this, savedInstanceState); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityCreated(this, + savedInstanceState); + } + } + } + + private void dispatchActivityPostCreated(@Nullable Bundle savedInstanceState) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostCreated(this, + savedInstanceState); + } + } + getApplication().dispatchActivityPostCreated(this, savedInstanceState); + } + + private void dispatchActivityPreStarted() { + getApplication().dispatchActivityPreStarted(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStarted(this); + } + } + } + + private void dispatchActivityStarted() { + getApplication().dispatchActivityStarted(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStarted(this); + } + } + } + + private void dispatchActivityPostStarted() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPostStarted(this); + } + } + getApplication().dispatchActivityPostStarted(this); + } + + private void dispatchActivityPreResumed() { + getApplication().dispatchActivityPreResumed(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreResumed(this); + } + } + } + + private void dispatchActivityResumed() { + getApplication().dispatchActivityResumed(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityResumed(this); + } + } + } + + private void dispatchActivityPostResumed() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostResumed(this); + } + } + getApplication().dispatchActivityPostResumed(this); + } + + private void dispatchActivityPrePaused() { + getApplication().dispatchActivityPrePaused(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPrePaused(this); + } + } + } + + private void dispatchActivityPaused() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPaused(this); + } + } + getApplication().dispatchActivityPaused(this); + } + + private void dispatchActivityPostPaused() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostPaused(this); + } + } + getApplication().dispatchActivityPostPaused(this); + } + + private void dispatchActivityPreStopped() { + getApplication().dispatchActivityPreStopped(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStopped(this); + } + } + } + + private void dispatchActivityStopped() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStopped(this); + } + } + getApplication().dispatchActivityStopped(this); + } + + private void dispatchActivityPostStopped() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPostStopped(this); + } + } + getApplication().dispatchActivityPostStopped(this); + } + + private void dispatchActivityPreSaveInstanceState(@NonNull Bundle outState) { + getApplication().dispatchActivityPreSaveInstanceState(this, outState); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPreSaveInstanceState(this, outState); + } + } + } + + private void dispatchActivitySaveInstanceState(@NonNull Bundle outState) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivitySaveInstanceState(this, outState); + } + } + getApplication().dispatchActivitySaveInstanceState(this, outState); + } + + private void dispatchActivityPostSaveInstanceState(@NonNull Bundle outState) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPostSaveInstanceState(this, outState); + } + } + getApplication().dispatchActivityPostSaveInstanceState(this, outState); + } + + private void dispatchActivityPreDestroyed() { + getApplication().dispatchActivityPreDestroyed(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPreDestroyed(this); + } + } + } + + private void dispatchActivityDestroyed() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityDestroyed(this); + } + } + getApplication().dispatchActivityDestroyed(this); + } + + private void dispatchActivityPostDestroyed() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPostDestroyed(this); + } + } + getApplication().dispatchActivityPostDestroyed(this); + } + + private Object[] collectActivityLifecycleCallbacks() { + Object[] callbacks = null; + synchronized (mActivityLifecycleCallbacks) { + if (mActivityLifecycleCallbacks.size() > 0) { + callbacks = mActivityLifecycleCallbacks.toArray(); + } + } + return callbacks; + } + + /** * Called when the activity is starting. This is where most initialization * should go: calling {@link #setContentView(int)} to inflate the * activity's UI, using {@link #findViewById} to programmatically interact @@ -1119,7 +1403,7 @@ public class Activity extends ContextThemeWrapper ? mLastNonConfigurationInstances.fragments : null); } mFragments.dispatchCreate(); - getApplication().dispatchActivityCreated(this, savedInstanceState); + dispatchActivityCreated(savedInstanceState); if (mVoiceInteractor != null) { mVoiceInteractor.attachActivity(this); } @@ -1355,7 +1639,7 @@ public class Activity extends ContextThemeWrapper mFragments.doLoaderStart(); - getApplication().dispatchActivityStarted(this); + dispatchActivityStarted(); if (mAutoFillResetNeeded) { getAutofillManager().onVisibleForAutofill(); @@ -1426,7 +1710,7 @@ public class Activity extends ContextThemeWrapper @CallSuper protected void onResume() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this); - getApplication().dispatchActivityResumed(this); + dispatchActivityResumed(); mActivityTransitionState.onResume(this, isTopOfTask()); enableAutofillCompatibilityIfNeeded(); if (mAutoFillResetNeeded) { @@ -1642,13 +1926,13 @@ public class Activity extends ContextThemeWrapper * @param outState The bundle to save the state to. */ final void performSaveInstanceState(@NonNull Bundle outState) { - getApplication().dispatchActivityPreSaveInstanceState(this, outState); + dispatchActivityPreSaveInstanceState(outState); onSaveInstanceState(outState); saveManagedDialogs(outState); mActivityTransitionState.saveState(outState); storeHasCurrentPermissionRequest(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState); - getApplication().dispatchActivityPostSaveInstanceState(this, outState); + dispatchActivityPostSaveInstanceState(outState); } /** @@ -1662,13 +1946,13 @@ public class Activity extends ContextThemeWrapper */ final void performSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) { - getApplication().dispatchActivityPreSaveInstanceState(this, outState); + dispatchActivityPreSaveInstanceState(outState); onSaveInstanceState(outState, outPersistentState); saveManagedDialogs(outState); storeHasCurrentPermissionRequest(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState + ", " + outPersistentState); - getApplication().dispatchActivityPostSaveInstanceState(this, outState); + dispatchActivityPostSaveInstanceState(outState); } /** @@ -1731,7 +2015,7 @@ public class Activity extends ContextThemeWrapper outState.putBoolean(AUTOFILL_RESET_NEEDED, true); getAutofillManager().onSaveInstanceState(outState); } - getApplication().dispatchActivitySaveInstanceState(this, outState); + dispatchActivitySaveInstanceState(outState); } /** @@ -1831,7 +2115,7 @@ public class Activity extends ContextThemeWrapper @CallSuper protected void onPause() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this); - getApplication().dispatchActivityPaused(this); + dispatchActivityPaused(); if (mAutoFillResetNeeded) { if (!mAutoFillIgnoreFirstResumePause) { if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this); @@ -2015,7 +2299,7 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this); if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); mActivityTransitionState.onStop(); - getApplication().dispatchActivityStopped(this); + dispatchActivityStopped(); mTranslucentCallback = null; mCalled = true; @@ -2104,7 +2388,7 @@ public class Activity extends ContextThemeWrapper mActionBar.onDestroy(); } - getApplication().dispatchActivityDestroyed(this); + dispatchActivityDestroyed(); notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED); @@ -7284,7 +7568,7 @@ public class Activity extends ContextThemeWrapper @UnsupportedAppUsage final void performCreate(Bundle icicle, PersistableBundle persistentState) { - getApplication().dispatchActivityPreCreated(this, icicle); + dispatchActivityPreCreated(icicle); mCanEnterPictureInPicture = true; restoreHasCurrentPermissionRequest(icicle); if (persistentState != null) { @@ -7299,7 +7583,7 @@ public class Activity extends ContextThemeWrapper com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); - getApplication().dispatchActivityPostCreated(this, icicle); + dispatchActivityPostCreated(icicle); } final void performNewIntent(@NonNull Intent intent) { @@ -7308,7 +7592,7 @@ public class Activity extends ContextThemeWrapper } final void performStart(String reason) { - getApplication().dispatchActivityPreStarted(this); + dispatchActivityPreStarted(); mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); mFragments.noteStateNotSaved(); mCalled = false; @@ -7351,7 +7635,7 @@ public class Activity extends ContextThemeWrapper } mActivityTransitionState.enterReady(this); - getApplication().dispatchActivityPostStarted(this); + dispatchActivityPostStarted(); } /** @@ -7406,7 +7690,7 @@ public class Activity extends ContextThemeWrapper } final void performResume(boolean followedByPause, String reason) { - getApplication().dispatchActivityPreResumed(this); + dispatchActivityPreResumed(); performRestart(true /* start */, reason); mFragments.execPendingActions(); @@ -7456,11 +7740,11 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPostResume()"); } - getApplication().dispatchActivityPostResumed(this); + dispatchActivityPostResumed(); } final void performPause() { - getApplication().dispatchActivityPrePaused(this); + dispatchActivityPrePaused(); mDoReportFullyDrawn = false; mFragments.dispatchPause(); mCalled = false; @@ -7473,7 +7757,7 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPause()"); } - getApplication().dispatchActivityPostPaused(this); + dispatchActivityPostPaused(); } final void performUserLeaving() { @@ -7489,7 +7773,7 @@ public class Activity extends ContextThemeWrapper mCanEnterPictureInPicture = false; if (!mStopped) { - getApplication().dispatchActivityPreStopped(this); + dispatchActivityPreStopped(); if (mWindow != null) { mWindow.closeAllPanels(); } @@ -7524,13 +7808,13 @@ public class Activity extends ContextThemeWrapper } mStopped = true; - getApplication().dispatchActivityPostStopped(this); + dispatchActivityPostStopped(); } mResumed = false; } final void performDestroy() { - getApplication().dispatchActivityPreDestroyed(this); + dispatchActivityPreDestroyed(); mDestroyed = true; mWindow.destroy(); mFragments.dispatchDestroy(); @@ -7540,7 +7824,7 @@ public class Activity extends ContextThemeWrapper if (mVoiceInteractor != null) { mVoiceInteractor.detachActivity(); } - getApplication().dispatchActivityPostDestroyed(this); + dispatchActivityPostDestroyed(); } final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode, diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 56ccf6f4a76f..6fdf7c8b4fac 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -433,4 +433,18 @@ public class ActivityTaskManager { } return sb.toString(); } + + /** + * Clears launch params for the given package. + * @param packageNames the names of the packages of which the launch params are to be cleared + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public void clearLaunchParamsForPackages(List<String> packageNames) { + try { + getService().clearLaunchParamsForPackages(packageNames); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 09b77d5b8d0a..777a4949a132 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -445,4 +445,9 @@ interface IActivityTaskManager { void setPackageScreenCompatMode(in String packageName, int mode); boolean getPackageAskScreenCompat(in String packageName); void setPackageAskScreenCompat(in String packageName, boolean ask); + + /** + * Clears launch params for given packages. + */ + void clearLaunchParamsForPackages(in List<String> packageNames); } diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS index 1c9a43acfa65..673d85fe79c5 100644 --- a/core/java/android/app/backup/OWNERS +++ b/core/java/android/app/backup/OWNERS @@ -1,7 +1,6 @@ -artikz@google.com +anniemeng@google.com brufino@google.com bryanmawhinney@google.com ctate@google.com jorlow@google.com -mkarpinski@google.com diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index ecdd810653ec..099d15ad61c2 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -22,6 +22,9 @@ import android.apex.ApexInfo; import android.os.Parcel; import android.os.Parcelable; +import java.util.ArrayList; +import java.util.List; + /** * Overall information about the contents of a package. This corresponds * to all of the information collected from AndroidManifest.xml. @@ -204,7 +207,10 @@ public class PackageInfo implements Parcelable { * {@link PackageManager#GET_PERMISSIONS} was set. This list includes * all permissions requested, even those that were not granted or known * by the system at install time. + * + * @deprecated Use {@link #usesPermissions} */ + @Deprecated public String[] requestedPermissions; /** @@ -214,10 +220,23 @@ public class PackageInfo implements Parcelable { * {@link PackageManager#GET_PERMISSIONS} was set. Each value matches * the corresponding entry in {@link #requestedPermissions}, and will have * the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate. + * + * @deprecated Use {@link #usesPermissions} */ + @Deprecated public int[] requestedPermissionsFlags; /** + * Array of all {@link android.R.styleable#AndroidManifestUsesPermission + * <uses-permission>} tags included under <manifest>, + * or null if there were none. This is only filled in if the flag + * {@link PackageManager#GET_PERMISSIONS} was set. This list includes + * all permissions requested, even those that were not granted or known + * by the system at install time. + */ + public UsesPermissionInfo[] usesPermissions; + + /** * Flag for {@link #requestedPermissionsFlags}: the requested permission * is required for the application to run; the user can not optionally * disable it. Currently all permissions are required. @@ -456,6 +475,7 @@ public class PackageInfo implements Parcelable { dest.writeTypedArray(permissions, parcelableFlags); dest.writeStringArray(requestedPermissions); dest.writeIntArray(requestedPermissionsFlags); + dest.writeTypedArray(usesPermissions, parcelableFlags); dest.writeTypedArray(signatures, parcelableFlags); dest.writeTypedArray(configPreferences, parcelableFlags); dest.writeTypedArray(reqFeatures, parcelableFlags); @@ -520,6 +540,7 @@ public class PackageInfo implements Parcelable { permissions = source.createTypedArray(PermissionInfo.CREATOR); requestedPermissions = source.createStringArray(); requestedPermissionsFlags = source.createIntArray(); + usesPermissions = source.createTypedArray(UsesPermissionInfo.CREATOR); signatures = source.createTypedArray(Signature.CREATOR); configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR); reqFeatures = source.createTypedArray(FeatureInfo.CREATOR); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index d00c9a036a53..49189e53f385 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -785,18 +785,23 @@ public class PackageParser { pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags); } } - N = p.requestedPermissions.size(); + N = p.usesPermissionInfos.size(); if (N > 0) { pi.requestedPermissions = new String[N]; pi.requestedPermissionsFlags = new int[N]; + pi.usesPermissions = new UsesPermissionInfo[N]; for (int i=0; i<N; i++) { - final String perm = p.requestedPermissions.get(i); + UsesPermissionInfo info = p.usesPermissionInfos.get(i); + final String perm = info.getPermission(); pi.requestedPermissions[i] = perm; + int permissionFlags = 0; // The notion of required permissions is deprecated but for compatibility. - pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED; + permissionFlags |= PackageInfo.REQUESTED_PERMISSION_REQUIRED; if (grantedPermissions != null && grantedPermissions.contains(perm)) { - pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED; + permissionFlags |= PackageInfo.REQUESTED_PERMISSION_GRANTED; } + pi.requestedPermissionsFlags[i] = permissionFlags; + pi.usesPermissions[i] = new UsesPermissionInfo(info, permissionFlags); } } } @@ -2114,12 +2119,12 @@ public class PackageParser { return null; } } else if (tagName.equals(TAG_USES_PERMISSION)) { - if (!parseUsesPermission(pkg, res, parser)) { + if (!parseUsesPermission(pkg, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M) || tagName.equals(TAG_USES_PERMISSION_SDK_23)) { - if (!parseUsesPermission(pkg, res, parser)) { + if (!parseUsesPermission(pkg, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_USES_CONFIGURATION)) { @@ -2442,7 +2447,7 @@ public class PackageParser { newPermsMsg.append(' '); } newPermsMsg.append(npi.name); - pkg.requestedPermissions.add(npi.name); + addRequestedPermission(pkg, npi.name); pkg.implicitPermissions.add(npi.name); } } @@ -2463,7 +2468,7 @@ public class PackageParser { for (int in = 0; in < newPerms.size(); in++) { final String perm = newPerms.get(in); if (!pkg.requestedPermissions.contains(perm)) { - pkg.requestedPermissions.add(perm); + addRequestedPermission(pkg, perm); pkg.implicitPermissions.add(perm); } } @@ -2543,13 +2548,13 @@ public class PackageParser { } } else { if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) { - pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO); + addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_AUDIO); } if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) { - pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO); + addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_VIDEO); } if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) { - pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES); + addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_IMAGES); } } @@ -2589,6 +2594,14 @@ public class PackageParser { } /** + * Helper method for adding a requested permission to a package outside of a uses-permission. + */ + private void addRequestedPermission(Package pkg, String permission) { + pkg.requestedPermissions.add(permission); + pkg.usesPermissionInfos.add(new UsesPermissionInfo(permission)); + } + + /** * Computes the targetSdkVersion to use at runtime. If the package is not * compatible with this platform, populates {@code outError[0]} with an * error message. @@ -2845,8 +2858,8 @@ public class PackageParser { return certSha256Digests; } - private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser) - throws XmlPullParserException, IOException { + private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser, + String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUsesPermission); @@ -2870,6 +2883,44 @@ public class PackageParser { final String requiredNotfeature = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0); + int dataSentOffDevice = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSentOffDevice, 0); + + int dataSharedWithThirdParty = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSharedWithThirdParty, 0); + + int dataUsedForMonetization = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesPermission_dataUsedForMonetization, 0); + + int retentionWeeks = -1; + int retention; + + String rawRetention = sa.getString( + com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime); + + if (rawRetention == null) { + retention = UsesPermissionInfo.RETENTION_UNDEFINED; + } else if ("notRetained".equals(rawRetention)) { + retention = UsesPermissionInfo.RETENTION_NOT_RETAINED; + } else if ("userSelected".equals(rawRetention)) { + retention = UsesPermissionInfo.RETENTION_USER_SELECTED; + } else if ("unlimited".equals(rawRetention)) { + retention = UsesPermissionInfo.RETENTION_UNLIMITED; + } else { + // A number of weeks was specified + retention = UsesPermissionInfo.RETENTION_SPECIFIED; + retentionWeeks = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime, + -1); + + if (retentionWeeks < 0) { + outError[0] = "Bad value provided for dataRetentionTime."; + mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; + XmlUtils.skipCurrentTag(parser); + sa.recycle(); + return false; + } + } sa.recycle(); XmlUtils.skipCurrentTag(parser); @@ -2902,6 +2953,10 @@ public class PackageParser { + parser.getPositionDescription()); } + UsesPermissionInfo info = new UsesPermissionInfo(name, dataSentOffDevice, + dataSharedWithThirdParty, dataUsedForMonetization, retention, retentionWeeks); + pkg.usesPermissionInfos.add(info); + return true; } @@ -3236,6 +3291,10 @@ public class PackageParser { perm.info.flags = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0); + perm.info.usageInfoRequired = sa.getInt( + com.android.internal.R.styleable.AndroidManifestPermission_usageInfoRequired, 0) + != 0; + sa.recycle(); if (perm.info.protectionLevel == -1) { @@ -6370,6 +6429,9 @@ public class PackageParser { @UnsupportedAppUsage public final ArrayList<String> requestedPermissions = new ArrayList<String>(); + public final ArrayList<UsesPermissionInfo> usesPermissionInfos = + new ArrayList<>(); + /** Permissions requested but not in the manifest. */ public final ArrayList<String> implicitPermissions = new ArrayList<>(); @@ -6900,6 +6962,7 @@ public class PackageParser { dest.readStringList(requestedPermissions); internStringArrayList(requestedPermissions); + dest.readParcelableList(usesPermissionInfos, boot); dest.readStringList(implicitPermissions); internStringArrayList(implicitPermissions); protectedBroadcasts = dest.createStringArrayList(); @@ -7066,6 +7129,7 @@ public class PackageParser { dest.writeParcelableList(instrumentation, flags); dest.writeStringList(requestedPermissions); + dest.writeParcelableList(usesPermissionInfos, flags); dest.writeStringList(implicitPermissions); dest.writeStringList(protectedBroadcasts); diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index 60c06a1e4d87..75239491a223 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -308,6 +309,12 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { */ public CharSequence nonLocalizedDescription; + /** + * If {@code true} an application targeting {@link Build.VERSION_CODES.Q} <em>must</em> + * include permission data usage information in order to be able to be granted this permission. + */ + public boolean usageInfoRequired; + /** @hide */ public static int fixProtectionLevel(int level) { if (level == PROTECTION_SIGNATURE_OR_SYSTEM) { @@ -394,6 +401,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { descriptionRes = orig.descriptionRes; requestRes = orig.requestRes; nonLocalizedDescription = orig.nonLocalizedDescription; + usageInfoRequired = orig.usageInfoRequired; } /** @@ -458,6 +466,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { dest.writeInt(descriptionRes); dest.writeInt(requestRes); TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags); + dest.writeInt(usageInfoRequired ? 1 : 0); } /** @hide */ @@ -498,5 +507,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { descriptionRes = source.readInt(); requestRes = source.readInt(); nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); + usageInfoRequired = source.readInt() != 0; } } diff --git a/core/java/android/content/pm/UsesPermissionInfo.java b/core/java/android/content/pm/UsesPermissionInfo.java new file mode 100644 index 000000000000..997552b4f9d5 --- /dev/null +++ b/core/java/android/content/pm/UsesPermissionInfo.java @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.RetentionPolicy; +/** + * Information you can retrive about a particular application requested permission. This + * corresponds to information collected from the AndroidManifest.xml's <uses-permission> + * tags. + */ +public final class UsesPermissionInfo extends PackageItemInfo implements Parcelable { + + /** + * Flag for {@link #flags}: the requested permission is currently granted to the application. + */ + public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 1 << 1; + + /** @hide */ + @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_REQUESTED_PERMISSION_GRANTED}) + @java.lang.annotation.Retention(RetentionPolicy.SOURCE) + public @interface Flags {} + + /** An unset value for {@link #getDataSentOffDevice()}, + * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()} + */ + public static final int USAGE_UNDEFINED = 0; + + /** + * A yes value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()}, + * and {@link #getDataUsedForMonetization()} corresponding to the <code>yes</code> value of + * {@link android.R.attrs#dataSentOffDevice}, {@link android.R.attrs#dataSharedWithThirdParty}, + * and {@link android.R.attrs#dataUsedForMonetization} attributes. + */ + public static final int USAGE_YES = 1; + + /** + * A user triggered only value for {@link #getDataSentOffDevice()}, + * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()} + * corresponding to the <code>userTriggered</code> value of + * {@link android.R.attrs#dataSentOffDevice}, {@link android.R.attrs#dataSharedWithThirdParty}, + * and {@link android.R.attrs#dataUsedForMonetization} attributes. + */ + public static final int USAGE_USER_TRIGGERED = 2; + + /** + * A no value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()}, + * and {@link #getDataUsedForMonetization()} corresponding to the <code>no</code> value of + * {@link android.R.attrs#dataSentOffDevice}, {@link android.R.attrs#dataSharedWithThirdParty}, + * and {@link android.R.attrs#dataUsedForMonetization} attributes. + */ + public static final int USAGE_NO = 3; + + /** @hide */ + @IntDef(prefix = {"USAGE_"}, value = { + USAGE_UNDEFINED, + USAGE_YES, + USAGE_USER_TRIGGERED, + USAGE_NO}) + @java.lang.annotation.Retention(RetentionPolicy.SOURCE) + public @interface Usage {} + + /** + * An unset value for {@link #getDataRetention}. + */ + public static final int RETENTION_UNDEFINED = 0; + + /** + * A data not retained value for {@link #getDataRetention()} corresponding to the + * <code>notRetained</code> value of {@link android.R.attrs#dataRetentionTime}. + */ + public static final int RETENTION_NOT_RETAINED = 1; + + /** + * A user selected value for {@link #getDataRetention()} corresponding to the + * <code>userSelected</code> value of {@link android.R.attrs#dataRetentionTime}. + */ + public static final int RETENTION_USER_SELECTED = 2; + + /** + * An unlimited value for {@link #getDataRetention()} corresponding to the + * <code>unlimited</code> value of {@link android.R.attrs#dataRetentionTime}. + */ + public static final int RETENTION_UNLIMITED = 3; + + /** + * A specified value for {@link #getDataRetention()} corresponding to providing the number of + * weeks data is retained in {@link android.R.attrs#dataRetentionTime}. The number of weeks + * is available in {@link #getDataRetentionWeeks()}. + */ + public static final int RETENTION_SPECIFIED = 4; + + /** @hide */ + @IntDef(prefix = {"RETENTION_"}, value = { + RETENTION_UNDEFINED, + RETENTION_NOT_RETAINED, + RETENTION_USER_SELECTED, + RETENTION_UNLIMITED, + RETENTION_SPECIFIED}) + @java.lang.annotation.Retention(RetentionPolicy.SOURCE) + public @interface Retention {} + + private final String mPermission; + private final @Flags int mFlags; + private final @Usage int mDataSentOffDevice; + private final @Usage int mDataSharedWithThirdParty; + private final @Usage int mDataUsedForMonetization; + private final @Retention int mDataRetention; + private final int mDataRetentionWeeks; + + /** @hide */ + public UsesPermissionInfo(String permission) { + mPermission = permission; + mDataSentOffDevice = USAGE_UNDEFINED; + mDataSharedWithThirdParty = USAGE_UNDEFINED; + mDataUsedForMonetization = USAGE_UNDEFINED; + mDataRetention = RETENTION_UNDEFINED; + mDataRetentionWeeks = -1; + mFlags = 0; + } + + /** @hide */ + public UsesPermissionInfo(String permission, + @Usage int dataSentOffDevice, @Usage int dataSharedWithThirdParty, + @Usage int dataUsedForMonetization, @Retention int dataRetention, + int dataRetentionWeeks) { + mPermission = permission; + mDataSentOffDevice = dataSentOffDevice; + mDataSharedWithThirdParty = dataSharedWithThirdParty; + mDataUsedForMonetization = dataUsedForMonetization; + mDataRetention = dataRetention; + mDataRetentionWeeks = dataRetentionWeeks; + mFlags = 0; + } + + /** @hide */ + public UsesPermissionInfo(UsesPermissionInfo orig) { + this(orig, orig.mFlags); + } + + /** @hide */ + public UsesPermissionInfo(UsesPermissionInfo orig, int flags) { + super(orig); + mPermission = orig.mPermission; + mFlags = flags; + mDataSentOffDevice = orig.mDataSentOffDevice; + mDataSharedWithThirdParty = orig.mDataSharedWithThirdParty; + mDataUsedForMonetization = orig.mDataUsedForMonetization; + mDataRetention = orig.mDataRetention; + mDataRetentionWeeks = orig.mDataRetentionWeeks; + } + + /** + * The name of the requested permission. + */ + public String getPermission() { + return mPermission; + } + + public @Flags int getFlags() { + return mFlags; + } + + /** + * If the application sends the data guarded by this permission off the device. + * + * See {@link android.R.attrs#dataSentOffDevice} + */ + public @Usage int getDataSentOffDevice() { + return mDataSentOffDevice; + } + + /** + * If the application or its services shares the data guarded by this permission with third + * parties. + * + * See {@link android.R.attrs#dataSharedWithThirdParty} + */ + public @Usage int getDataSharedWithThirdParty() { + return mDataSharedWithThirdParty; + } + + /** + * If the application or its services use the data guarded by this permission for monetization + * purposes. + * + * See {@link android.R.attrs#dataUsedForMonetization} + */ + public @Usage int getDataUsedForMonetization() { + return mDataUsedForMonetization; + } + + /** + * How long the application or its services store the data guarded by this permission. + * If set to {@link #RETENTION_SPECIFIED} {@link #getDataRetentionWeeks()} will contain the + * number of weeks the data is stored. + * + * See {@link android.R.attrs#dataRetentionTime} + */ + public @Retention int getDataRetention() { + return mDataRetention; + } + + /** + * If {@link #getDataRetention()} is {@link #RETENTION_SPECIFIED} the number of weeks the + * application or its services store data guarded by this permission. + * + * @throws IllegalStateException if {@link #getDataRetention} is not + * {@link #RETENTION_SPECIFIED}. + */ + public int getDataRetentionWeeks() { + if (mDataRetention != RETENTION_SPECIFIED) { + throw new IllegalStateException("Data retention weeks not specified"); + } + return mDataRetentionWeeks; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(mPermission); + dest.writeInt(mFlags); + dest.writeInt(mDataSentOffDevice); + dest.writeInt(mDataSharedWithThirdParty); + dest.writeInt(mDataUsedForMonetization); + dest.writeInt(mDataRetention); + dest.writeInt(mDataRetentionWeeks); + } + + private UsesPermissionInfo(Parcel source) { + super(source); + mPermission = source.readString(); + mFlags = source.readInt(); + mDataSentOffDevice = source.readInt(); + mDataSharedWithThirdParty = source.readInt(); + mDataUsedForMonetization = source.readInt(); + mDataRetention = source.readInt(); + mDataRetentionWeeks = source.readInt(); + } + + public static final Creator<UsesPermissionInfo> CREATOR = + new Creator<UsesPermissionInfo>() { + @Override + public UsesPermissionInfo createFromParcel(Parcel source) { + return new UsesPermissionInfo(source); + } + @Override + public UsesPermissionInfo[] newArray(int size) { + return new UsesPermissionInfo[size]; + } + }; +} diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 979bcae94bbd..a095b0d8b239 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -57,6 +57,7 @@ import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; +import android.view.InsetsState; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -185,6 +186,7 @@ public abstract class WallpaperService extends Service { final DisplayCutout.ParcelableWrapper mDisplayCutout = new DisplayCutout.ParcelableWrapper(); DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT; + final InsetsState mInsetsState = new InsetsState(); final MergedConfiguration mMergedConfiguration = new MergedConfiguration(); final WindowManager.LayoutParams mLayout @@ -808,9 +810,11 @@ public abstract class WallpaperService extends Service { mLayout.windowAnimations = com.android.internal.R.style.Animation_Wallpaper; mInputChannel = new InputChannel(); + if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets, - mOutsets, mDisplayCutout, mInputChannel) < 0) { + mOutsets, mDisplayCutout, mInputChannel, + mInsetsState) < 0) { Log.w(TAG, "Failed to add window while updating wallpaper surface."); return; } @@ -836,7 +840,8 @@ public abstract class WallpaperService extends Service { mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets, mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame, - mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface); + mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface, + mInsetsState); if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface + ", frame=" + mWinFrame); diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 4b8b7f304b0f..af41b6942a5e 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -24,6 +24,7 @@ import android.view.DragEvent; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.DisplayCutout; +import android.view.InsetsState; import com.android.internal.os.IResultReceiver; import android.util.MergedConfiguration; @@ -53,6 +54,12 @@ oneway interface IWindow { in MergedConfiguration newMergedConfiguration, in Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId, in DisplayCutout.ParcelableWrapper displayCutout); + + /** + * Called when the window insets configuration has changed. + */ + void insetsChanged(in InsetsState insetsState); + void moved(int newX, int newY); void dispatchAppVisibility(boolean visible); void dispatchGetNewSurface(); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index bedfa9ff133c..97625869209d 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -28,6 +28,7 @@ import android.view.IWindow; import android.view.IWindowId; import android.view.MotionEvent; import android.view.WindowManager; +import android.view.InsetsState; import android.view.Surface; import android.view.SurfaceControl; @@ -40,10 +41,11 @@ interface IWindowSession { int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets, - out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel); + out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel, + out InsetsState insetsState); int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, out Rect outContentInsets, - out Rect outStableInsets); + out Rect outStableInsets, out InsetsState insetsState); void remove(IWindow window); /** @@ -86,6 +88,7 @@ interface IWindowSession { * config for window, if it is now becoming visible and the merged configuration has changed * since it was last displayed. * @param outSurface Object in which is placed the new display surface. + * @param insetsState The current insets state in the system. * * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS}, * {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}. @@ -96,7 +99,8 @@ interface IWindowSession { out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets, out Rect outOutsets, out Rect outBackdropFrame, out DisplayCutout.ParcelableWrapper displayCutout, - out MergedConfiguration outMergedConfiguration, out Surface outSurface); + out MergedConfiguration outMergedConfiguration, out Surface outSurface, + out InsetsState insetsState); /* * Notify the window manager that an application is relaunching and diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java new file mode 100644 index 000000000000..7841d0417a2b --- /dev/null +++ b/core/java/android/view/InsetsController.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Rect; + +import java.io.PrintWriter; + +/** + * Implements {@link WindowInsetsController} on the client. + */ +class InsetsController { + + private final InsetsState mState = new InsetsState(); + private final Rect mFrame = new Rect(); + + void onFrameChanged(Rect frame) { + mFrame.set(frame); + } + + public InsetsState getState() { + return mState; + } + + public void setState(InsetsState state) { + mState.set(state); + } + + /** + * @see InsetsState#calculateInsets + */ + WindowInsets calculateInsets(boolean isScreenRound, + boolean alwaysConsumeNavBar, DisplayCutout cutout) { + return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout); + } + + void dump(String prefix, PrintWriter pw) { + pw.println(prefix); pw.println("InsetsController:"); + mState.dump(prefix + " ", pw); + } +} diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java new file mode 100644 index 000000000000..0cb8ad72f102 --- /dev/null +++ b/core/java/android/view/InsetsSource.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Insets; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.InsetsState.InternalInsetType; + +import java.io.PrintWriter; + +/** + * Represents the state of a single window generating insets for clients. + * @hide + */ +public class InsetsSource implements Parcelable { + + private final @InternalInsetType int mType; + + /** Frame of the source in screen coordinate space */ + private final Rect mFrame; + private boolean mVisible; + + private final Rect mTmpFrame = new Rect(); + + public InsetsSource(@InternalInsetType int type) { + mType = type; + mFrame = new Rect(); + } + + public InsetsSource(InsetsSource other) { + mType = other.mType; + mFrame = new Rect(other.mFrame); + mVisible = other.mVisible; + } + + public void setFrame(Rect frame) { + mFrame.set(frame); + } + + public void setVisible(boolean visible) { + mVisible = visible; + } + + public @InternalInsetType int getType() { + return mType; + } + + public Rect getFrame() { + return mFrame; + } + + /** + * Calculates the insets this source will cause to a client window. + * + * @param relativeFrame The frame to calculate the insets relative to. + * @param ignoreVisibility If true, always reports back insets even if source isn't visible. + * @return The resulting insets. + */ + public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) { + if (!ignoreVisibility && !mVisible) { + return Insets.NONE; + } + if (!mTmpFrame.setIntersect(mFrame, relativeFrame)) { + return Insets.NONE; + } + + // Intersecting at top/bottom + if (mTmpFrame.width() == relativeFrame.width()) { + if (mTmpFrame.top == relativeFrame.top) { + return Insets.of(0, mTmpFrame.height(), 0, 0); + } else { + return Insets.of(0, 0, 0, mTmpFrame.height()); + } + } + // Intersecting at left/right + else if (mTmpFrame.height() == relativeFrame.height()) { + if (mTmpFrame.left == relativeFrame.left) { + return Insets.of(mTmpFrame.width(), 0, 0, 0); + } else { + return Insets.of(0, 0, mTmpFrame.width(), 0); + } + } else { + return Insets.NONE; + } + } + + public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); + pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType)); + pw.print(" frame="); pw.print(mFrame.toShortString()); + pw.print(" visible="); pw.print(mVisible); + pw.println(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + InsetsSource that = (InsetsSource) o; + + if (mType != that.mType) return false; + if (mVisible != that.mVisible) return false; + return mFrame.equals(that.mFrame); + } + + @Override + public int hashCode() { + int result = mType; + result = 31 * result + mFrame.hashCode(); + result = 31 * result + (mVisible ? 1 : 0); + return result; + } + + public InsetsSource(Parcel in) { + mType = in.readInt(); + mFrame = in.readParcelable(null /* loader */); + mVisible = in.readBoolean(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeParcelable(mFrame, 0 /* flags*/); + dest.writeBoolean(mVisible); + } + + public static final Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() { + + public InsetsSource createFromParcel(Parcel in) { + return new InsetsSource(in); + } + + public InsetsSource[] newArray(int size) { + return new InsetsSource[size]; + } + }; +} diff --git a/core/java/android/view/InsetsState.aidl b/core/java/android/view/InsetsState.aidl new file mode 100644 index 000000000000..d02ddd15a8c9 --- /dev/null +++ b/core/java/android/view/InsetsState.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +parcelable InsetsState; diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java new file mode 100644 index 000000000000..9895adcad23a --- /dev/null +++ b/core/java/android/view/InsetsState.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.IntDef; +import android.graphics.Insets; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Holder for state of system windows that cause window insets for all other windows in the system. + * @hide + */ +public class InsetsState implements Parcelable { + + /** + * Internal representation of inset source types. This is different from the public API in + * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows + * at the same time. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "TYPE", value = { + TYPE_TOP_BAR, + TYPE_SIDE_BAR_1, + TYPE_SIDE_BAR_2, + TYPE_SIDE_BAR_3, + TYPE_IME + }) + public @interface InternalInsetType {} + + static final int FIRST_TYPE = 0; + + /** Top bar. Can be status bar or caption in freeform windowing mode. */ + public static final int TYPE_TOP_BAR = FIRST_TYPE; + + /** + * Up to 3 side bars that appear on left/right/bottom. On phones there is only one side bar + * (the navigation bar, see {@link #TYPE_NAVIGATION_BAR}), but other form factors might have + * multiple, like Android Auto. + */ + public static final int TYPE_SIDE_BAR_1 = 1; + public static final int TYPE_SIDE_BAR_2 = 2; + public static final int TYPE_SIDE_BAR_3 = 3; + + /** Input method window. */ + public static final int TYPE_IME = 4; + static final int LAST_TYPE = TYPE_IME; + + // Derived types + + /** First side bar is navigation bar. */ + public static final int TYPE_NAVIGATION_BAR = TYPE_SIDE_BAR_1; + + /** A shelf is the same as the navigation bar. */ + public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR; + + private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>(); + + public InsetsState() { + } + + /** + * Calculates {@link WindowInsets} based on the current source configuration. + * + * @param frame The frame to calculate the insets relative to. + * @return The calculated insets. + */ + public WindowInsets calculateInsets(Rect frame, boolean isScreenRound, + boolean alwaysConsumeNavBar, DisplayCutout cutout) { + Insets systemInsets = Insets.NONE; + Insets maxInsets = Insets.NONE; + final Rect relativeFrame = new Rect(frame); + final Rect relativeFrameMax = new Rect(frame); + for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { + InsetsSource source = mSources.get(type); + if (source == null) { + continue; + } + systemInsets = processSource(source, systemInsets, relativeFrame, + false /* ignoreVisibility */); + + // IME won't be reported in max insets as the size depends on the EditorInfo of the IME + // target. + if (source.getType() != TYPE_IME) { + maxInsets = processSource(source, maxInsets, relativeFrameMax, + true /* ignoreVisibility */); + } + } + return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound, + alwaysConsumeNavBar, cutout); + } + + private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame, + boolean ignoreVisibility) { + Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility); + insets = Insets.add(currentInsets, insets); + relativeFrame.inset(insets); + return insets; + } + + public InsetsSource getSource(@InternalInsetType int type) { + return mSources.computeIfAbsent(type, InsetsSource::new); + } + + /** + * Modifies the state of this class to exclude a certain type to make it ready for dispatching + * to the client. + * + * @param type The {@link InternalInsetType} of the source to remove + */ + public void removeSource(int type) { + mSources.remove(type); + } + + public void set(InsetsState other) { + set(other, false /* copySources */); + } + + public void set(InsetsState other, boolean copySources) { + mSources.clear(); + if (copySources) { + for (int i = 0; i < other.mSources.size(); i++) { + InsetsSource source = other.mSources.valueAt(i); + mSources.put(source.getType(), new InsetsSource(source)); + } + } else { + mSources.putAll(other.mSources); + } + } + + public void dump(String prefix, PrintWriter pw) { + pw.println(prefix + "InsetsState"); + for (int i = mSources.size() - 1; i >= 0; i--) { + mSources.valueAt(i).dump(prefix + " ", pw); + } + } + + static String typeToString(int type) { + switch (type) { + case TYPE_TOP_BAR: + return "TYPE_TOP_BAR"; + case TYPE_SIDE_BAR_1: + return "TYPE_SIDE_BAR_1"; + case TYPE_SIDE_BAR_2: + return "TYPE_SIDE_BAR_2"; + case TYPE_SIDE_BAR_3: + return "TYPE_SIDE_BAR_3"; + case TYPE_IME: + return "TYPE_IME"; + default: + return "TYPE_UNKNOWN"; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + InsetsState state = (InsetsState) o; + + if (mSources.size() != state.mSources.size()) { + return false; + } + for (int i = mSources.size() - 1; i >= 0; i--) { + InsetsSource source = mSources.valueAt(i); + InsetsSource otherSource = state.mSources.get(source.getType()); + if (otherSource == null) { + return false; + } + if (!otherSource.equals(source)) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return mSources.hashCode(); + } + + public InsetsState(Parcel in) { + readFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSources.size()); + for (int i = 0; i < mSources.size(); i++) { + dest.writeParcelable(mSources.valueAt(i), 0 /* flags */); + } + } + + public static final Creator<InsetsState> CREATOR = new Creator<InsetsState>() { + + public InsetsState createFromParcel(Parcel in) { + return new InsetsState(in); + } + + public InsetsState[] newArray(int size) { + return new InsetsState[size]; + } + }; + + public void readFromParcel(Parcel in) { + mSources.clear(); + final int size = in.readInt(); + for (int i = 0; i < size; i++) { + final InsetsSource source = in.readParcelable(null /* loader */); + mSources.put(source.getType(), source); + } + } +} + diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8e9f2d75bda0..d9d52c01924f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -161,6 +161,19 @@ public final class ViewRootImpl implements ViewParent, private static final boolean MT_RENDERER_AVAILABLE = true; /** + * If set to true, the view system will switch from using rectangles retrieved from window to + * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets + * directly from the full configuration, enabling richer information about the insets state, as + * well as new APIs to control it frame-by-frame, and synchronize animations with it. + * <p> + * Only switch this to true once the new insets system is productionized and the old APIs are + * fully migrated over. + */ + private static final String USE_NEW_INSETS_PROPERTY = "persist.wm.new_insets"; + private static final boolean USE_NEW_INSETS = + SystemProperties.getBoolean(USE_NEW_INSETS_PROPERTY, false); + + /** * Set this system property to true to force the view hierarchy to render * at 60 Hz. This can be used to measure the potential framerate. */ @@ -432,6 +445,8 @@ public final class ViewRootImpl implements ViewParent, boolean mAdded; boolean mAddedTouchMode; + final Rect mTmpFrame = new Rect(); + // These are accessed by multiple threads. final Rect mWinFrame; // frame given by window manager. @@ -444,6 +459,7 @@ public final class ViewRootImpl implements ViewParent, final DisplayCutout.ParcelableWrapper mPendingDisplayCutout = new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT); boolean mPendingAlwaysConsumeNavBar; + private InsetsState mPendingInsets; final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets = new ViewTreeObserver.InternalInsetsInfo(); @@ -531,6 +547,8 @@ public final class ViewRootImpl implements ViewParent, InputEventConsistencyVerifier.isInstrumentationEnabled() ? new InputEventConsistencyVerifier(this, 0) : null; + private final InsetsController mInsetsController = new InsetsController(); + static final class SystemUiVisibilityInfo { int seq; int globalVisibility; @@ -797,9 +815,11 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, - getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, + getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, - mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); + mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel, + mInsetsController.getState()); + setFrame(mTmpFrame); } catch (RemoteException e) { mAdded = false; mView = null; @@ -826,6 +846,7 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mAlwaysConsumeNavBar = (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0; mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar; + mPendingInsets = mInsetsController.getState(); if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow); if (res < WindowManagerGlobal.ADD_OKAY) { mAttachInfo.mRootView = null; @@ -1771,7 +1792,8 @@ public final class ViewRootImpl implements ViewParent, Rect stableInsets = mDispatchStableInsets; DisplayCutout displayCutout = mDispatchDisplayCutout; // For dispatch we preserve old logic, but for direct requests from Views we allow to - // immediately use pending insets. + // immediately use pending insets. This is such that getRootWindowInsets returns the + // result from the layout hint before we ran a traversal shortly after adding a window. if (!forceConstruct && (!mPendingContentInsets.equals(contentInsets) || !mPendingStableInsets.equals(stableInsets) || @@ -1788,10 +1810,16 @@ public final class ViewRootImpl implements ViewParent, } contentInsets = ensureInsetsNonNegative(contentInsets, "content"); stableInsets = ensureInsetsNonNegative(stableInsets, "stable"); - mLastWindowInsets = new WindowInsets(contentInsets, - null /* windowDecorInsets */, stableInsets, - mContext.getResources().getConfiguration().isScreenRound(), - mAttachInfo.mAlwaysConsumeNavBar, displayCutout); + if (USE_NEW_INSETS) { + mLastWindowInsets = mInsetsController.calculateInsets( + mContext.getResources().getConfiguration().isScreenRound(), + mAttachInfo.mAlwaysConsumeNavBar, displayCutout); + } else { + mLastWindowInsets = new WindowInsets(contentInsets, + null /* windowDecorInsets */, stableInsets, + mContext.getResources().getConfiguration().isScreenRound(), + mAttachInfo.mAlwaysConsumeNavBar, displayCutout); + } } return mLastWindowInsets; } @@ -1991,6 +2019,9 @@ public final class ViewRootImpl implements ViewParent, if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) { insetsChanged = true; } + if (!mPendingInsets.equals(mInsetsController.getState())) { + insetsChanged = true; + } if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { windowSizeMayChange = true; @@ -2184,6 +2215,8 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mStableInsets); final boolean cutoutChanged = !mPendingDisplayCutout.equals( mAttachInfo.mDisplayCutout); + final boolean insetsStateChanged = !mPendingInsets.equals( + mInsetsController.getState()); final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets); final boolean surfaceSizeChanged = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0; @@ -2221,6 +2254,10 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar; contentInsetsChanged = true; } + if (insetsStateChanged) { + mInsetsController.setState(mPendingInsets); + contentInsetsChanged = true; + } if (contentInsetsChanged || mLastSystemUiVisibility != mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested || mLastOverscanRequested != mAttachInfo.mOverscanRequested @@ -2666,7 +2703,6 @@ public final class ViewRootImpl implements ViewParent, } private void maybeHandleWindowMove(Rect frame) { - // TODO: Well, we are checking whether the frame has changed similarly // to how this is done for the insets. This is however incorrect since // the insets and the frame are translated. For example, the old frame @@ -4171,6 +4207,7 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_UPDATE_POINTER_ICON = 27; private final static int MSG_POINTER_CAPTURE_CHANGED = 28; private final static int MSG_DRAW_FINISHED = 29; + private final static int MSG_INSETS_CHANGED = 30; final class ViewRootHandler extends Handler { @Override @@ -4226,6 +4263,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_POINTER_CAPTURE_CHANGED"; case MSG_DRAW_FINISHED: return "MSG_DRAW_FINISHED"; + case MSG_INSETS_CHANGED: + return "MSG_INSETS_CHANGED"; } return super.getMessageName(message); } @@ -4306,7 +4345,7 @@ public final class ViewRootImpl implements ViewParent, || !mPendingVisibleInsets.equals(args.arg3) || !mPendingOutsets.equals(args.arg7); - mWinFrame.set((Rect) args.arg1); + setFrame((Rect) args.arg1); mPendingOverscanInsets.set((Rect) args.arg5); mPendingContentInsets.set((Rect) args.arg2); mPendingStableInsets.set((Rect) args.arg6); @@ -4329,16 +4368,25 @@ public final class ViewRootImpl implements ViewParent, requestLayout(); } break; + case MSG_INSETS_CHANGED: + mPendingInsets = (InsetsState) msg.obj; + + // TODO: Full traversal not needed here + if (USE_NEW_INSETS) { + requestLayout(); + } + break; case MSG_WINDOW_MOVED: if (mAdded) { final int w = mWinFrame.width(); final int h = mWinFrame.height(); final int l = msg.arg1; final int t = msg.arg2; - mWinFrame.left = l; - mWinFrame.right = l + w; - mWinFrame.top = t; - mWinFrame.bottom = t + h; + mTmpFrame.left = l; + mTmpFrame.right = l + w; + mTmpFrame.top = t; + mTmpFrame.bottom = t + h; + setFrame(mTmpFrame); mPendingBackDropFrame.set(mWinFrame); maybeHandleWindowMove(mWinFrame); @@ -6724,9 +6772,9 @@ public final class ViewRootImpl implements ViewParent, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, - mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, + mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout, - mPendingMergedConfiguration, mSurface); + mPendingMergedConfiguration, mSurface, mPendingInsets); mPendingAlwaysConsumeNavBar = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0; @@ -6736,15 +6784,22 @@ public final class ViewRootImpl implements ViewParent, } if (mTranslator != null) { - mTranslator.translateRectInScreenToAppWinFrame(mWinFrame); + mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame); mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets); mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets); } + setFrame(mTmpFrame); + return relayoutResult; } + private void setFrame(Rect frame) { + mWinFrame.set(frame); + mInsetsController.onFrameChanged(frame); + } + /** * {@inheritDoc} */ @@ -6847,6 +6902,8 @@ public final class ViewRootImpl implements ViewParent, mChoreographer.dump(prefix, writer); + mInsetsController.dump(prefix, writer); + writer.print(prefix); writer.println("View Hierarchy:"); dumpViewHierarchy(innerPrefix, writer, mView); } @@ -7055,6 +7112,10 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } + private void dispatchInsetsChanged(InsetsState insetsState) { + mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget(); + } + public void dispatchMoved(int newX, int newY) { if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY); if (mTranslator != null) { @@ -8118,6 +8179,14 @@ public final class ViewRootImpl implements ViewParent, } @Override + public void insetsChanged(InsetsState insetsState) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchInsetsChanged(insetsState); + } + } + + @Override public void moved(int newX, int newY) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java index b9d53c1b5884..d78bfac1f878 100644 --- a/core/java/com/android/internal/os/SomeArgs.java +++ b/core/java/com/android/internal/os/SomeArgs.java @@ -120,6 +120,8 @@ public final class SomeArgs { arg5 = null; arg6 = null; arg7 = null; + arg8 = null; + arg9 = null; argi1 = 0; argi2 = 0; argi3 = 0; diff --git a/core/java/com/android/internal/util/function/NonaConsumer.java b/core/java/com/android/internal/util/function/NonaConsumer.java new file mode 100644 index 000000000000..3e7ce2b405a7 --- /dev/null +++ b/core/java/com/android/internal/util/function/NonaConsumer.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Consumer; + +/** + * A 9-argument {@link Consumer} + * + * @hide + */ +public interface NonaConsumer<A, B, C, D, E, F, G, H, I> { + void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i); +} diff --git a/core/java/com/android/internal/util/function/NonaFunction.java b/core/java/com/android/internal/util/function/NonaFunction.java new file mode 100644 index 000000000000..560b4f157ee1 --- /dev/null +++ b/core/java/com/android/internal/util/function/NonaFunction.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Function; + +/** + * A 9-argument {@link Function} + * + * @hide + */ +public interface NonaFunction<A, B, C, D, E, F, G, H, I, R> { + R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i); +} diff --git a/core/java/com/android/internal/util/function/NonaPredicate.java b/core/java/com/android/internal/util/function/NonaPredicate.java new file mode 100644 index 000000000000..c1e6f377e7ae --- /dev/null +++ b/core/java/com/android/internal/util/function/NonaPredicate.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Predicate; + +/** + * A 9-argument {@link Predicate} + * + * @hide + */ +public interface NonaPredicate<A, B, C, D, E, F, G, H, I> { + boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i); +} diff --git a/core/java/com/android/internal/util/function/OctConsumer.java b/core/java/com/android/internal/util/function/OctConsumer.java new file mode 100644 index 000000000000..83ee30530c26 --- /dev/null +++ b/core/java/com/android/internal/util/function/OctConsumer.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Consumer; + +/** + * A 8-argument {@link Consumer} + * + * @hide + */ +public interface OctConsumer<A, B, C, D, E, F, G, H> { + void accept(A a, B b, C c, D d, E e, F f, G g, H h); +} diff --git a/core/java/com/android/internal/util/function/OctFunction.java b/core/java/com/android/internal/util/function/OctFunction.java new file mode 100644 index 000000000000..cb16624725b7 --- /dev/null +++ b/core/java/com/android/internal/util/function/OctFunction.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Function; + +/** + * A 8-argument {@link Function} + * + * @hide + */ +public interface OctFunction<A, B, C, D, E, F, G, H, R> { + R apply(A a, B b, C c, D d, E e, F f, G g, H h); +} diff --git a/core/java/com/android/internal/util/function/OctPredicate.java b/core/java/com/android/internal/util/function/OctPredicate.java new file mode 100644 index 000000000000..7f36d6acc066 --- /dev/null +++ b/core/java/com/android/internal/util/function/OctPredicate.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Predicate; + +/** + * A 8-argument {@link Predicate} + * + * @hide + */ +public interface OctPredicate<A, B, C, D, E, F, G, H> { + boolean test(A a, B b, C c, D d, E e, F f, G g, H h); +} diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java index 4ffe44194958..d74e715605bb 100755 --- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java +++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java @@ -22,6 +22,10 @@ import com.android.internal.util.function.HeptConsumer; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; +import com.android.internal.util.function.NonaConsumer; +import com.android.internal.util.function.NonaFunction; +import com.android.internal.util.function.OctConsumer; +import com.android.internal.util.function.OctFunction; import com.android.internal.util.function.QuadConsumer; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.QuintConsumer; @@ -39,61 +43,62 @@ import java.util.function.Function; * * @hide */ -abstract class OmniFunction<A, B, C, D, E, F, G, R> implements +abstract class OmniFunction<A, B, C, D, E, F, G, H, I, R> implements PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>, QuadFunction<A, B, C, D, R>, QuintFunction<A, B, C, D, E, R>, HexFunction<A, B, C, D, E, F, R>, HeptFunction<A, B, C, D, E, F, G, R>, + OctFunction<A, B, C, D, E, F, G, H, R>, NonaFunction<A, B, C, D, E, F, G, H, I, R>, PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>, QuintConsumer<A, B, C, D, E>, HexConsumer<A, B, C, D, E, F>, - HeptConsumer<A, B, C, D, E, F, G>, - PooledPredicate<A>, BiPredicate<A, B>, + HeptConsumer<A, B, C, D, E, F, G>, OctConsumer<A, B, C, D, E, F, G, H>, + NonaConsumer<A, B, C, D, E, F, G, H, I>, PooledPredicate<A>, BiPredicate<A, B>, PooledSupplier<R>, PooledRunnable, ThrowingRunnable, ThrowingSupplier<R>, PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble { - abstract R invoke(A a, B b, C c, D d, E e, F f, G g); + abstract R invoke(A a, B b, C c, D d, E e, F f, G g, H h, I i); @Override public R apply(A o, B o2) { - return invoke(o, o2, null, null, null, null, null); + return invoke(o, o2, null, null, null, null, null, null, null); } @Override public R apply(A o) { - return invoke(o, null, null, null, null, null, null); + return invoke(o, null, null, null, null, null, null, null, null); } - public abstract <V> OmniFunction<A, B, C, D, E, F, G, V> andThen( + public abstract <V> OmniFunction<A, B, C, D, E, F, G, H, I, V> andThen( Function<? super R, ? extends V> after); - public abstract OmniFunction<A, B, C, D, E, F, G, R> negate(); + public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> negate(); @Override public void accept(A o, B o2) { - invoke(o, o2, null, null, null, null, null); + invoke(o, o2, null, null, null, null, null, null, null); } @Override public void accept(A o) { - invoke(o, null, null, null, null, null, null); + invoke(o, null, null, null, null, null, null, null, null); } @Override public void run() { - invoke(null, null, null, null, null, null, null); + invoke(null, null, null, null, null, null, null, null, null); } @Override public R get() { - return invoke(null, null, null, null, null, null, null); + return invoke(null, null, null, null, null, null, null, null, null); } @Override public boolean test(A o, B o2) { - return (Boolean) invoke(o, o2, null, null, null, null, null); + return (Boolean) invoke(o, o2, null, null, null, null, null, null, null); } @Override public boolean test(A o) { - return (Boolean) invoke(o, null, null, null, null, null, null); + return (Boolean) invoke(o, null, null, null, null, null, null, null, null); } @Override @@ -108,52 +113,72 @@ abstract class OmniFunction<A, B, C, D, E, F, G, R> implements @Override public R apply(A a, B b, C c) { - return invoke(a, b, c, null, null, null, null); + return invoke(a, b, c, null, null, null, null, null, null); } @Override public void accept(A a, B b, C c) { - invoke(a, b, c, null, null, null, null); + invoke(a, b, c, null, null, null, null, null, null); } @Override public R apply(A a, B b, C c, D d) { - return invoke(a, b, c, d, null, null, null); + return invoke(a, b, c, d, null, null, null, null, null); } @Override public R apply(A a, B b, C c, D d, E e) { - return invoke(a, b, c, d, e, null, null); + return invoke(a, b, c, d, e, null, null, null, null); } @Override public R apply(A a, B b, C c, D d, E e, F f) { - return invoke(a, b, c, d, e, f, null); + return invoke(a, b, c, d, e, f, null, null, null); } @Override public R apply(A a, B b, C c, D d, E e, F f, G g) { - return invoke(a, b, c, d, e, f, g); + return invoke(a, b, c, d, e, f, g, null, null); + } + + @Override + public R apply(A a, B b, C c, D d, E e, F f, G g, H h) { + return invoke(a, b, c, d, e, f, g, h, null); + } + + @Override + public R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i) { + return invoke(a, b, c, d, e, f, g, h, i); } @Override public void accept(A a, B b, C c, D d) { - invoke(a, b, c, d, null, null, null); + invoke(a, b, c, d, null, null, null, null, null); } @Override public void accept(A a, B b, C c, D d, E e) { - invoke(a, b, c, d, e, null, null); + invoke(a, b, c, d, e, null, null, null, null); } @Override public void accept(A a, B b, C c, D d, E e, F f) { - invoke(a, b, c, d, e, f, null); + invoke(a, b, c, d, e, f, null, null, null); } @Override public void accept(A a, B b, C c, D d, E e, F f, G g) { - invoke(a, b, c, d, e, f, g); + invoke(a, b, c, d, e, f, g, null, null); + } + + @Override + public void accept(A a, B b, C c, D d, E e, F f, G g, H h) { + invoke(a, b, c, d, e, f, g, h, null); + } + + @Override + public void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i) { + invoke(a, b, c, d, e, f, g, h, i); } @Override @@ -167,5 +192,5 @@ abstract class OmniFunction<A, B, C, D, E, F, G, R> implements } @Override - public abstract OmniFunction<A, B, C, D, E, F, G, R> recycleOnUse(); + public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> recycleOnUse(); } diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java index af3c7527c432..c00932e7a8aa 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java @@ -25,6 +25,10 @@ import com.android.internal.util.function.HeptConsumer; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; +import com.android.internal.util.function.NonaConsumer; +import com.android.internal.util.function.NonaFunction; +import com.android.internal.util.function.OctConsumer; +import com.android.internal.util.function.OctFunction; import com.android.internal.util.function.QuadConsumer; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.QuintConsumer; @@ -176,7 +180,8 @@ public interface PooledLambda { Consumer<? super A> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null); + function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null, + null); } /** @@ -192,7 +197,8 @@ public interface PooledLambda { Predicate<? super A> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null); + function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null, + null); } /** @@ -208,7 +214,8 @@ public interface PooledLambda { Function<? super A, ? extends R> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null); + function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null, + null); } /** @@ -238,7 +245,8 @@ public interface PooledLambda { A arg1) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null); + function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -257,7 +265,8 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null); + function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -274,7 +283,8 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null); + function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -291,7 +301,8 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null); + function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -308,7 +319,8 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -325,7 +337,8 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -342,7 +355,8 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -359,7 +373,8 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -376,7 +391,8 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -393,7 +409,8 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -424,7 +441,8 @@ public interface PooledLambda { A arg1, B arg2) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null); + function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -444,7 +462,8 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); + function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -462,7 +481,8 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); + function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -480,7 +500,8 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -498,7 +519,8 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -516,7 +538,8 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -534,7 +557,8 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -552,7 +576,8 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -570,7 +595,8 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -602,7 +628,8 @@ public interface PooledLambda { A arg1, B arg2, C arg3) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); + function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -623,7 +650,8 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -642,7 +670,8 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -661,7 +690,8 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -680,7 +710,8 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -699,7 +730,8 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -718,7 +750,8 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -737,7 +770,8 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -756,7 +790,8 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -775,7 +810,8 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -794,7 +830,8 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -827,7 +864,8 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -849,7 +887,8 @@ public interface PooledLambda { QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function, A arg1, B arg2, C arg3, D arg4, E arg5) { return acquire(PooledLambdaImpl.sPool, - function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null); + function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null, + null); } /** @@ -869,7 +908,8 @@ public interface PooledLambda { QuintFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5) { return acquire(PooledLambdaImpl.sPool, - function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null); + function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null, + null); } /** @@ -904,7 +944,8 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null); + function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -927,7 +968,8 @@ public interface PooledLambda { HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { return acquire(PooledLambdaImpl.sPool, - function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null); + function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null, + null); } /** @@ -948,7 +990,8 @@ public interface PooledLambda { HexFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { return acquire(PooledLambdaImpl.sPool, - function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null); + function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null, + null); } /** @@ -984,7 +1027,8 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null); + function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -1008,7 +1052,8 @@ public interface PooledLambda { HeptConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { return acquire(PooledLambdaImpl.sPool, - function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null, + null); } /** @@ -1031,7 +1076,8 @@ public interface PooledLambda { ? super G, ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { return acquire(PooledLambdaImpl.sPool, - function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null, + null); } /** @@ -1068,7 +1114,195 @@ public interface PooledLambda { ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null, + null); + return Message.obtain().setCallback(callback.recycleOnUse()); + } + } + + /** + * {@link PooledRunnable} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) } + */ + static <A, B, C, D, E, F, G, H> PooledRunnable obtainRunnable( + OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G, + ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, + H arg8) { + return acquire(PooledLambdaImpl.sPool, + function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + null); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) } + */ + static <A, B, C, D, E, F, G, H, R> PooledSupplier<R> obtainSupplier( + OctFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? extends R> function, + A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) { + return acquire(PooledLambdaImpl.sPool, + function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + null); + } + + /** + * Factory of {@link Message}s that contain an + * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its + * {@link Message#getCallback internal callback}. + * + * The callback is equivalent to one obtainable via + * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)} + * + * Note that using this method with {@link android.os.Handler#handleMessage} + * is more efficient than the alternative of {@link android.os.Handler#post} + * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points + * when obtaining {@link Message} and {@link PooledRunnable} from pools separately + * + * You may optionally set a {@link Message#what} for the message if you want to be + * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise + * there's no need to do so + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6, + * arg7, arg8) } when handled + */ + static <A, B, C, D, E, F, G, H> Message obtainMessage( + OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G, + ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, + H arg8) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + null); + return Message.obtain().setCallback(callback.recycleOnUse()); + } + } + + /** + * {@link PooledRunnable} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @param arg9 parameter supplied to {@code function} on call + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) } + */ + static <A, B, C, D, E, F, G, H, I> PooledRunnable obtainRunnable( + NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4, + E arg5, F arg6, G arg7, H arg8, I arg9) { + return acquire(PooledLambdaImpl.sPool, + function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + arg9); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @param arg9 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) } + */ + static <A, B, C, D, E, F, G, H, I, R> PooledSupplier<R> obtainSupplier( + NonaFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? super I, ? extends R> function, + A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) { + return acquire(PooledLambdaImpl.sPool, + function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + arg9); + } + + /** + * Factory of {@link Message}s that contain an + * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its + * {@link Message#getCallback internal callback}. + * + * The callback is equivalent to one obtainable via + * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)} + * + * Note that using this method with {@link android.os.Handler#handleMessage} + * is more efficient than the alternative of {@link android.os.Handler#post} + * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points + * when obtaining {@link Message} and {@link PooledRunnable} from pools separately + * + * You may optionally set a {@link Message#what} for the message if you want to be + * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise + * there's no need to do so + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @param arg9 parameter supplied to {@code function} on call + * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6, + * arg7, arg8, arg9) } when handled + */ + static <A, B, C, D, E, F, G, H, I> Message obtainMessage( + NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4, + E arg5, F arg6, G arg7, H arg8, I arg9) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + arg9); return Message.obtain().setCallback(callback.recycleOnUse()); } } diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java index eea1e5f0ac5c..6be626a5134a 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java @@ -30,6 +30,12 @@ import com.android.internal.util.function.HeptPredicate; import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.HexPredicate; +import com.android.internal.util.function.NonaConsumer; +import com.android.internal.util.function.NonaFunction; +import com.android.internal.util.function.NonaPredicate; +import com.android.internal.util.function.OctConsumer; +import com.android.internal.util.function.OctFunction; +import com.android.internal.util.function.OctPredicate; import com.android.internal.util.function.QuadConsumer; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.QuadPredicate; @@ -54,12 +60,12 @@ import java.util.function.Supplier; * @hide */ final class PooledLambdaImpl<R> extends OmniFunction<Object, - Object, Object, Object, Object, Object, Object, R> { + Object, Object, Object, Object, Object, Object, Object, Object, R> { private static final boolean DEBUG = false; private static final String LOG_TAG = "PooledLambdaImpl"; - private static final int MAX_ARGS = 7; + private static final int MAX_ARGS = 9; private static final int MAX_POOL_SIZE = 50; @@ -125,7 +131,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, /** * Bit schema: - * AAAAAAABCDEEEEEEFFFFFF + * AAAAAAAAABCDEEEEEEFFFFFF * * Where: * A - whether {@link #mArgs arg} at corresponding index was specified at @@ -161,17 +167,19 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } @Override - R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { + R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, + Object a8, Object a9) { checkNotRecycled(); if (DEBUG) { Log.i(LOG_TAG, this + ".invoke(" + commaSeparateFirstN( - new Object[] { a1, a2, a3, a4, a5, a6, a7 }, + new Object[] { a1, a2, a3, a4, a5, a6, a7, a8, a9 }, LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS))) + ")"); } - final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) - && fillInArg(a4) && fillInArg(a5) && fillInArg(a6) && fillInArg(a7); + final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4) + && fillInArg(a5) && fillInArg(a6) && fillInArg(a7) && fillInArg(a8) + && fillInArg(a9); int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE)); if (argCount != LambdaType.MASK_ARG_COUNT) { for (int i = 0; i < argCount; i++) { @@ -335,7 +343,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, popArg(2), popArg(3), popArg(4), popArg(5)); } } - } + } break; case 7: { switch (returnType) { @@ -356,7 +364,49 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, popArg(5), popArg(6)); } } - } + } break; + + case 8: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((OctConsumer) mFunc).accept(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), + popArg(5), popArg(6), popArg(7)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((OctPredicate) mFunc).test(popArg(0), + popArg(1), popArg(2), popArg(3), + popArg(4), popArg(5), popArg(6), popArg(7)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((OctFunction) mFunc).apply(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), + popArg(5), popArg(6), popArg(7)); + } + } + } break; + + case 9: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((NonaConsumer) mFunc).accept(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), popArg(5), + popArg(6), popArg(7), popArg(8)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((NonaPredicate) mFunc).test(popArg(0), + popArg(1), popArg(2), popArg(3), popArg(4), + popArg(5), popArg(6), popArg(7), popArg(8)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((NonaFunction) mFunc).apply(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), popArg(5), + popArg(6), popArg(7), popArg(8)); + } + } + } break; } throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType)); } @@ -419,8 +469,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, * Internal non-typesafe factory method for {@link PooledLambdaImpl} */ static <E extends PooledLambda> E acquire(Pool pool, Object func, - int fNumArgs, int numPlaceholders, int fReturnType, - Object a, Object b, Object c, Object d, Object e, Object f, Object g) { + int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c, + Object d, Object e, Object f, Object g, Object h, Object i) { PooledLambdaImpl r = acquire(pool); if (DEBUG) { Log.i(LOG_TAG, @@ -436,6 +486,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, + ", e = " + e + ", f = " + f + ", g = " + g + + ", h = " + h + + ", i = " + i + ")"); } r.mFunc = func; @@ -449,6 +501,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, setIfInBounds(r.mArgs, 4, e); setIfInBounds(r.mArgs, 5, f); setIfInBounds(r.mArgs, 6, g); + setIfInBounds(r.mArgs, 7, h); + setIfInBounds(r.mArgs, 8, i); return (E) r; } @@ -474,13 +528,14 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } @Override - public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> negate() { + public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object, + R> negate() { throw new UnsupportedOperationException(); } @Override - public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, V> andThen( - Function<? super R, ? extends V> after) { + public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object, + V> andThen(Function<? super R, ? extends V> after) { throw new UnsupportedOperationException(); } @@ -500,7 +555,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } @Override - public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> recycleOnUse() { + public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object, + R> recycleOnUse() { if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()"); mFlags |= FLAG_RECYCLE_ON_USE; return this; @@ -584,6 +640,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, case 5: return "Quint"; case 6: return "Hex"; case 7: return "Hept"; + case 8: return "Oct"; + case 9: return "Nona"; default: throw new IllegalArgumentException("" + argCount); } } diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 137ca7f2ac27..36fe4fc5af49 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -27,6 +27,7 @@ import android.view.DragEvent; import android.view.IWindow; import android.view.IWindowSession; import android.view.PointerIcon; +import android.view.InsetsState; import com.android.internal.os.IResultReceiver; @@ -53,6 +54,10 @@ public class BaseIWindow extends IWindow.Stub { } @Override + public void insetsChanged(InsetsState insetsState) { + } + + @Override public void moved(int newX, int newY) { } diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index e89b5933fdc8..752624b0a0be 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -468,6 +468,10 @@ static jboolean android_view_RenderNode_getAllowForceDark(jlong renderNodePtr) { return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getAllowForceDark(); } +static jlong android_view_RenderNode_getUniqueId(jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->uniqueId(); +} + // ---------------------------------------------------------------------------- // RenderProperties - Animations // ---------------------------------------------------------------------------- @@ -694,6 +698,7 @@ static const JNINativeMethod gMethods[] = { { "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight }, { "nSetAllowForceDark", "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark }, { "nGetAllowForceDark", "(J)Z", (void*) android_view_RenderNode_getAllowForceDark }, + { "nGetUniqueId", "(J)J", (void*) android_view_RenderNode_getUniqueId }, }; int register_android_view_RenderNode(JNIEnv* env) { diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 18d1d5dbdbe3..d5eaa00ef491 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1601,6 +1601,10 @@ <attr name="request" /> <attr name="protectionLevel" /> <attr name="permissionFlags" /> + <!-- If {@code true} applications that target Q <em>must</em> specify the permission usage + attributes in their {@code uses-permission} elements or the permission will not be + granted. --> + <attr name="usageInfoRequired" format="boolean" /> </declare-styleable> <!-- The <code>permission-group</code> tag declares a logical grouping of @@ -1700,6 +1704,81 @@ requested. If it does support the feature, it will be as if the manifest didn't request it at all. --> <attr name="requiredNotFeature" format="string" /> + + <!-- Specify if the app uploads data, or derived data, guarded by this permission. + + If the permission is defined with {@link AndroidManifestPermission#usageRequired} + {@code true} this <em>must</em> be specified by apps that target Android Q or the + permission will not be granted, it will be as if the manifest didn't request it at all. + --> + <attr name="dataSentOffDevice"> + <!-- The application may send data, or derived data, guarded by this permission off of the + device. --> + <enum name="yes" value="1" /> + <!-- The application may send data, or derived data, guarded by this permission off of the + device, however it will only do so when explicitly triggered by a user action. --> + <enum name="userTriggered" value="2" /> + <!-- The application does not send data, or derived data, guarded by this permission off + of the device. --> + <enum name="no" value="3" /> + </attr> + + <!-- Specify if the application or its related off-device services provide data, + or derived data, guarded by this permission to third parties outside of the developer's + organization that do not qualify as data processors. + + If the permission is defined with {@link AndroidManifestPermission#usageRequired} + {@code true} this <em>must</em> be specified by apps that target Android Q or the + permission will not be granted, it will be as if the manifest didn't request it at all. + --> + <attr name="dataSharedWithThirdParty"> + <!-- The application or its services may provide data, or derived data, guarded by this + permission to third party organizations. --> + <enum name="yes" value="1" /> + <!-- The application or its services may provide data, or derived data, guarded by this + permission to third party organizations, however it will only do so when explicitly + triggered by a user action. --> + <enum name="userTriggered" value="2" /> + <!-- The application or its services does not provide data, or derived data, guarded by + this permission to third party organizations. --> + <enum name="no" value="3" /> + </attr> + + <!-- Specify if the application or its related off-device services use data, + or derived data, guarded by this permission for monetization purposes. + + For example, if the data is sold to another party or used for targeting advertisements + this must be set to {@code yes}. + + If the permission is defined with {@link AndroidManifestPermission#usageRequired} + {@code true} this <em>must</em> be specified by apps that target Android Q or the + permission will not be granted, it will be as if the manifest didn't request it at all. + --> + <attr name="dataUsedForMonetization"> + <!-- The application or its services may use data, or derived data, guarded by this + permission for monetization purposes. --> + <enum name="yes" value="1" /> + <!-- The application or its services may use data, or derived data, guarded by this + permission for monetization purposes, however it will only do so when explicity + triggered by a user action. --> + <enum name="userTriggered" value="2" /> + <!-- The application or its services does not use data, or derived data, guarded by + this permission for monetization purposes. --> + <enum name="no" value="3" /> + </attr> + + <!-- Specify how long the application or its related off-device services store + data, or derived data, guarded by this permission. + + This can be one of "notRetained", "userSelected", "unlimited", or a number + representing the number of weeks the data is retained. + + If the permission is defined with {@link AndroidManifestPermission#usageRequired} + {@code true} this <em>must</em> be specified by apps that target Android Q or the + permission will not be granted, it will be as if the manifest didn't request it at all. + --> + <attr name="dataRetentionTime" format="string" /> + </declare-styleable> <!-- The <code>uses-configuration</code> tag specifies diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 73e0f85227d7..feefcad3f56f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2923,6 +2923,11 @@ <public name="importantForContentCapture" /> <public name="supportsMultipleDisplays" /> <public name="useAppZygote" /> + <public name="usageInfoRequired" /> + <public name="dataSentOffDevice" /> + <public name="dataSharedWithThirdParty" /> + <public name="dataUsedForMonetization" /> + <public name="dataRetentionTime" /> </public-group> <public-group type="drawable" first-id="0x010800b4"> diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java new file mode 100644 index 000000000000..ed472d2a7f64 --- /dev/null +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static junit.framework.Assert.assertEquals; + +import android.graphics.Insets; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.FlakyTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@FlakyTest(detail = "Promote once confirmed non-flaky") +@RunWith(AndroidJUnit4.class) +public class InsetsSourceTest { + + private InsetsSource mSource = new InsetsSource(TYPE_NAVIGATION_BAR); + + @Before + public void setUp() { + mSource.setVisible(true); + } + + @Test + public void testCalculateInsetsTop() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + + @Test + public void testCalculateInsetsBottom() { + mSource.setFrame(new Rect(0, 400, 500, 500)); + Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(0, 0, 0, 100), insets); + } + + @Test + public void testCalculateInsetsLeft() { + mSource.setFrame(new Rect(0, 0, 100, 500)); + Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(100, 0, 0, 0), insets); + } + + @Test + public void testCalculateInsetsRight() { + mSource.setFrame(new Rect(400, 0, 500, 500)); + Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(0, 0, 100, 0), insets); + } + + @Test + public void testCalculateInsets_overextend() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + + @Test + public void testCalculateInsets_invisible() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + mSource.setVisible(false); + Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(0, 0, 0, 0), insets); + } + + @Test + public void testCalculateInsets_ignoreVisibility() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + mSource.setVisible(false); + Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500), + true /* ignoreVisibility */); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + + // Parcel and equals already tested via InsetsStateTest +} diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java new file mode 100644 index 000000000000..6bb9539e89bd --- /dev/null +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static android.view.InsetsState.TYPE_IME; +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static android.view.InsetsState.TYPE_TOP_BAR; +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.graphics.Rect; +import android.os.Parcel; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.FlakyTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@FlakyTest(detail = "Promote once confirmed non-flaky") +@RunWith(AndroidJUnit4.class) +public class InsetsStateTest { + + private InsetsState mState = new InsetsState(); + private InsetsState mState2 = new InsetsState(); + + @Test + public void testCalculateInsets() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_TOP_BAR).setVisible(true); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(TYPE_IME).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT); + assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets()); + } + + @Test + public void testCalculateInsets_imeAndNav() { + mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300)); + mState.getSource(TYPE_IME).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT); + assertEquals(100, insets.getStableInsetBottom()); + assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets()); + } + + @Test + public void testCalculateInsets_navRightStatusTop() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_TOP_BAR).setVisible(true); + mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); + mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT); + assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets()); + } + + @Test + public void testStripForDispatch() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_TOP_BAR).setVisible(true); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(TYPE_IME).setVisible(true); + mState.removeSource(TYPE_IME); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT); + assertEquals(0, insets.getSystemWindowInsetBottom()); + } + + @Test + public void testEquals_differentRect() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 10, 10)); + assertNotEquals(mState, mState2); + } + + @Test + public void testEquals_differentSource() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + assertNotEquals(mState, mState2); + } + + @Test + public void testEquals_sameButDifferentInsertOrder() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + assertEquals(mState, mState2); + } + + @Test + public void testEquals_visibility() { + mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_IME).setVisible(true); + mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + assertNotEquals(mState, mState2); + } + + @Test + public void testParcelUnparcel() { + mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_IME).setVisible(true); + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + Parcel p = Parcel.obtain(); + mState.writeToParcel(p, 0 /* flags */); + mState2.readFromParcel(p); + p.recycle(); + assertEquals(mState, mState2); + } +} diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java index de110c849338..d9da27c8b931 100644 --- a/graphics/java/android/graphics/Insets.java +++ b/graphics/java/android/graphics/Insets.java @@ -82,6 +82,17 @@ public final class Insets { } /** + * Add two Insets. + * + * @param a The first Insets to add. + * @param b The second Insets to add. + * @return a + b, i. e. all insets on every side are added together. + */ + public static @NonNull Insets add(@NonNull Insets a, @NonNull Insets b) { + return Insets.of(a.left + b.left, a.top + b.top, a.right + b.right, a.bottom + b.bottom); + } + + /** * Two Insets instances are equal iff they belong to the same class and their fields are * pairwise equal. * diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index c4dc0adb3be0..40a32f3429dc 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -106,6 +106,20 @@ public final class Rect implements Parcelable { } /** + * @hide + */ + public Rect(@Nullable Insets r) { + if (r == null) { + left = top = right = bottom = 0; + } else { + left = r.left; + top = r.top; + right = r.right; + bottom = r.bottom; + } + } + + /** * Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise. * * @hide @@ -418,6 +432,18 @@ public final class Rect implements Parcelable { } /** + * Insets the rectangle on all sides specified by the dimensions of {@code insets}. + * @hide + * @param insets The insets to inset the rect by. + */ + public void inset(Insets insets) { + left += insets.left; + top += insets.top; + right -= insets.right; + bottom -= insets.bottom; + } + + /** * Insets the rectangle on all sides specified by the insets. * @hide * @param left The amount to add from the rectangle's left diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index 45d7a2178b84..d6f08b92a648 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -1173,6 +1173,22 @@ public final class RenderNode { return nGetAllowForceDark(mNativeRenderNode); } + /** + * Returns the unique ID that identifies this RenderNode. This ID is unique for the + * lifetime of the process. IDs are reset on process death, and are unique only within + * the process. + * + * This ID is intended to be used with debugging tools to associate a particular + * RenderNode across different debug dumping & inspection tools. For example + * a View layout inspector should include the unique ID for any RenderNodes that it owns + * to associate the drawing content with the layout content. + * + * @return the unique ID for this RenderNode + */ + public long getUniqueId() { + return nGetUniqueId(mNativeRenderNode); + } + /////////////////////////////////////////////////////////////////////////// // Animations /////////////////////////////////////////////////////////////////////////// @@ -1479,4 +1495,7 @@ public final class RenderNode { @CriticalNative private static native boolean nGetAllowForceDark(long renderNode); + + @CriticalNative + private static native long nGetUniqueId(long renderNode); } diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index caf610b8c236..5bd59d479876 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -713,11 +713,12 @@ public abstract class Drawable { } /** - * Whether this drawable requests projection. + * Whether this drawable requests projection. Indicates that the + * {@link android.graphics.RenderNode} this Drawable will draw into should be drawn immediately + * after the closest ancestor RenderNode containing a projection receiver. * - * @hide magic! + * @see android.graphics.RenderNode#setProjectBackwards(boolean) */ - @UnsupportedAppUsage public boolean isProjected() { return false; } diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index d2a8f02cc6a7..4a639102192f 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -29,6 +29,7 @@ #include <SkPathOps.h> #include <algorithm> +#include <atomic> #include <sstream> #include <string> @@ -47,8 +48,14 @@ private: TreeInfo* mTreeInfo; }; +static int64_t generateId() { + static std::atomic<int64_t> sNextId{1}; + return sNextId++; +} + RenderNode::RenderNode() - : mDirtyPropertyFields(0) + : mUniqueId(generateId()) + , mDirtyPropertyFields(0) , mNeedsDisplayListSync(false) , mDisplayList(nullptr) , mStagingDisplayList(nullptr) @@ -444,5 +451,38 @@ const SkPath* RenderNode::getClippedOutline(const SkRect& clipRect) const { return &mClippedOutlineCache.clippedOutline; } +using StringBuffer = FatVector<char, 128>; + +template <typename... T> +static void format(StringBuffer& buffer, const std::string_view& format, T... args) { + buffer.resize(buffer.capacity()); + while (1) { + int needed = snprintf(buffer.data(), buffer.size(), + format.data(), std::forward<T>(args)...); + if (needed < 0) { + buffer[0] = '\0'; + buffer.resize(1); + return; + } + if (needed < buffer.size()) { + buffer.resize(needed + 1); + return; + } + buffer.resize(buffer.size() * 2); + } +} + +void RenderNode::markDrawStart(SkCanvas& canvas) { + StringBuffer buffer; + format(buffer, "RenderNode(id=%d, name='%s')", uniqueId(), getName()); + canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr); +} + +void RenderNode::markDrawEnd(SkCanvas& canvas) { + StringBuffer buffer; + format(buffer, "/RenderNode(id=%d, name='%s')", uniqueId(), getName()); + canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index be0b46b1c45f..6060123ed759 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -213,6 +213,11 @@ public: UsageHint usageHint() const { return mUsageHint; } + int64_t uniqueId() const { return mUniqueId; } + + void markDrawStart(SkCanvas& canvas); + void markDrawEnd(SkCanvas& canvas); + private: void computeOrderingImpl(RenderNodeOp* opState, std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, @@ -233,6 +238,7 @@ private: void incParentRefCount() { mParentCount++; } void decParentRefCount(TreeObserver& observer, TreeInfo* info = nullptr); + const int64_t mUniqueId; String8 mName; sp<VirtualLightRefBase> mUserContext; diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index ea14d11b7b3e..d80cb6d1ab70 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -115,12 +115,26 @@ void RenderNodeDrawable::onDraw(SkCanvas* canvas) { } } +class MarkDraw { +public: + explicit MarkDraw(SkCanvas& canvas, RenderNode& node) : mCanvas(canvas), mNode(node) { + if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { + mNode.markDrawStart(mCanvas); + } + } + ~MarkDraw() { + if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { + mNode.markDrawEnd(mCanvas); + } + } +private: + SkCanvas& mCanvas; + RenderNode& mNode; +}; + void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { RenderNode* renderNode = mRenderNode.get(); - if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { - SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight()); - canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr); - } + MarkDraw _marker{*canvas, *renderNode}; // We only respect the nothingToDraw check when we are composing a layer. This // ensures that we paint the layer even if it is not currently visible in the diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml index 06782633a4de..789d185b8b42 100644 --- a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml +++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml @@ -72,7 +72,6 @@ </LinearLayout> <LinearLayout - android:id="@+id/entity_header_links" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_centerVertical="true" @@ -85,6 +84,7 @@ android:layout_width="wrap_content" android:layout_weight="1" android:layout_height="0dp" + android:visibility="gone" android:minWidth="24dp" android:src="@null" android:tint="?android:attr/colorAccent"/> @@ -95,6 +95,7 @@ android:layout_width="wrap_content" android:layout_weight="1" android:layout_height="0dp" + android:visibility="gone" android:minWidth="24dp" android:src="@null" android:tint="?android:attr/colorAccent"/> diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java index 8b434a546504..496aa0e572ae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java @@ -323,7 +323,9 @@ public class QSDetail extends LinearLayout { post(new Runnable() { @Override public void run() { - handleShowingDetail(detail, x, y, false /* toggleQs */); + if (isAttachedToWindow()) { + handleShowingDetail(detail, x, y, false /* toggleQs */); + } } }); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index 0638998d8e67..3a96595dee06 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -198,7 +198,8 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { mIcon.setIcon(state, allowAnimations); setContentDescription(state.contentDescription); - mAccessibilityClass = state.expandedAccessibilityClassName; + mAccessibilityClass = + state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName; if (state instanceof QSTile.BooleanState) { boolean newState = ((BooleanState) state).value; if (mTileState != newState) { diff --git a/services/backup/OWNERS b/services/backup/OWNERS index 645723e655f8..d1dbbffc6708 100644 --- a/services/backup/OWNERS +++ b/services/backup/OWNERS @@ -1,7 +1,5 @@ anniemeng@google.com -artikz@google.com brufino@google.com bryanmawhinney@google.com ctate@google.com jorlow@google.com -mkarpinski@google.com diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 7750c3781067..9ed978f67006 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -554,7 +554,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final Handler mHandler; @VisibleForTesting - public final Handler mUidEventHandler; + final Handler mUidEventHandler; private final ServiceThread mUidEventThread; @@ -1465,7 +1465,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @VisibleForTesting - public void updateNetworks() throws InterruptedException { + void updateNetworks() throws InterruptedException { updateNetworksInternal(); final CountDownLatch latch = new CountDownLatch(1); mHandler.post(() -> { @@ -1510,7 +1510,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * @return cycleDay to use in the mobile NetworkPolicy. */ @VisibleForTesting - public int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config, + int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config, int fallbackCycleDay) { if (config == null) { return fallbackCycleDay; @@ -1542,7 +1542,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * @return warningBytes to use in the mobile NetworkPolicy. */ @VisibleForTesting - public long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config, + long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config, long fallbackWarningBytes) { if (config == null) { return fallbackWarningBytes; @@ -1575,7 +1575,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * @return limitBytes to use in the mobile NetworkPolicy. */ @VisibleForTesting - public long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config, + long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config, long fallbackLimitBytes) { if (config == null) { return fallbackLimitBytes; @@ -2039,7 +2039,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @VisibleForTesting - public NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) { + NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) { final NetworkTemplate template = buildTemplateMobileAll(subscriberId); final RecurrenceRule cycleRule = NetworkPolicy .buildRule(ZonedDateTime.now().getDayOfMonth(), ZoneId.systemDefault()); @@ -3489,7 +3489,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @VisibleForTesting - public boolean isUidForeground(int uid) { + boolean isUidForeground(int uid) { synchronized (mUidRulesFirstLock) { return isUidStateForeground( mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY)); @@ -3931,7 +3931,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * power saving restrictions may still apply. */ @VisibleForTesting - public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) { + void setAppIdleWhitelist(int uid, boolean shouldWhitelist) { synchronized (mUidRulesFirstLock) { if (mAppIdleTempWhitelistAppIds.get(uid) == shouldWhitelist) { // No change. @@ -3956,7 +3956,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Return the list of UIDs currently in the app idle whitelist. */ @VisibleForTesting - public int[] getAppIdleWhitelist() { + int[] getAppIdleWhitelist() { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); synchronized (mUidRulesFirstLock) { @@ -3971,7 +3971,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Returns if the UID is currently considered idle. */ @VisibleForTesting - public boolean isUidIdle(int uid) { + boolean isUidIdle(int uid) { synchronized (mUidRulesFirstLock) { if (mAppIdleTempWhitelistAppIds.get(uid)) { // UID is temporarily whitelisted. @@ -4844,13 +4844,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @VisibleForTesting - public void addIdleHandler(IdleHandler handler) { + void addIdleHandler(IdleHandler handler) { mHandler.getLooper().getQueue().addIdleHandler(handler); } @GuardedBy("mUidRulesFirstLock") @VisibleForTesting - public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) { + void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) { mRestrictBackgroundPowerState = result; boolean restrictBackground = result.batterySaverEnabled; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index f279af03753e..94d276c8496d 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -309,6 +309,7 @@ public class ZenModeHelper { newConfig = mConfig.copy(); ZenRule rule = new ZenRule(); populateZenRule(automaticZenRule, rule, true); + newConfig.automaticRules.put(rule.id, rule); if (setConfigLocked(newConfig, reason, rule.component, true)) { return rule.id; } else { diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index e194d1541ea7..2d583ca39adb 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -105,6 +105,8 @@ public final class BasePermission { */ private boolean perUser; + boolean usageInfoRequired; + public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) { name = _name; sourcePackageName = _sourcePackageName; @@ -351,6 +353,7 @@ public final class BasePermission { } if (bp.perm == p) { bp.protectionLevel = p.info.protectionLevel; + bp.usageInfoRequired = p.info.usageInfoRequired; } if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) { Log.d(TAG, " Permissions: " + r); @@ -430,6 +433,7 @@ public final class BasePermission { permissionInfo.packageName = sourcePackageName; permissionInfo.nonLocalizedLabel = name; permissionInfo.protectionLevel = protectionLevel; + permissionInfo.usageInfoRequired = usageInfoRequired; return permissionInfo; } @@ -458,6 +462,7 @@ public final class BasePermission { bp.protectionLevel = readInt(parser, null, "protection", PermissionInfo.PROTECTION_NORMAL); bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel); + bp.usageInfoRequired = readInt(parser, null, "usageInfoRequired", 0) != 0; if (dynamic) { final PermissionInfo pi = new PermissionInfo(); pi.packageName = sourcePackage.intern(); @@ -465,6 +470,7 @@ public final class BasePermission { pi.icon = readInt(parser, null, "icon", 0); pi.nonLocalizedLabel = parser.getAttributeValue(null, "label"); pi.protectionLevel = bp.protectionLevel; + pi.usageInfoRequired = bp.usageInfoRequired; bp.pendingPermissionInfo = pi; } out.put(bp.name, bp); @@ -497,6 +503,7 @@ public final class BasePermission { if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) { serializer.attribute(null, "protection", Integer.toString(protectionLevel)); } + serializer.attribute(null, "usageInfoRequired", usageInfoRequired ? "1" : "0"); if (type == BasePermission.TYPE_DYNAMIC) { final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo; if (pi != null) { @@ -533,6 +540,7 @@ public final class BasePermission { if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false; // We'll take care of setting this one. if (!compareStrings(pi1.packageName, pi2.packageName)) return false; + if (pi1.usageInfoRequired != pi2.usageInfoRequired) return false; // These are not currently stored in settings. //if (!compareStrings(pi1.group, pi2.group)) return false; //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false; @@ -580,6 +588,8 @@ public final class BasePermission { pw.print(" enforced="); pw.println(readEnforced); } + pw.print(" usageInfoRequired="); + pw.println(usageInfoRequired); return true; } } diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 4124210b27cb..b390eebf3d7e 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -30,6 +30,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.ResultReceiver; +import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManagerInternal; import android.text.TextUtils; @@ -42,6 +44,7 @@ import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.SystemService; +import java.io.FileDescriptor; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -334,5 +337,13 @@ public class RoleManagerService extends SystemService { return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, false, true, name, null); } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, + FileDescriptor err, String[] args, ShellCallback callback, + ResultReceiver resultReceiver) { + (new RoleManagerShellCommand(this)).exec( + this, in, out, err, args, callback, resultReceiver); + } } } diff --git a/services/core/java/com/android/server/role/RoleManagerShellCommand.java b/services/core/java/com/android/server/role/RoleManagerShellCommand.java new file mode 100644 index 000000000000..e1977ef083b4 --- /dev/null +++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java @@ -0,0 +1,134 @@ +/* + * 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.role; + +import android.app.role.IRoleManager; +import android.app.role.IRoleManagerCallback; +import android.os.RemoteException; +import android.os.ShellCommand; +import android.os.UserHandle; + +import java.io.PrintWriter; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +class RoleManagerShellCommand extends ShellCommand { + private final IRoleManager mRoleManager; + + RoleManagerShellCommand(IRoleManager roleManager) { + mRoleManager = roleManager; + } + + private class Callback extends IRoleManagerCallback.Stub { + private final CompletableFuture<Void> mResult = new CompletableFuture<>(); + + public int waitForResult() { + try { + mResult.get(5, TimeUnit.SECONDS); + return 0; + } catch (Exception e) { + getErrPrintWriter().println("Error: " + e.toString()); + return -1; + } + } + + @Override + public void onSuccess() { + mResult.complete(null); + } + + @Override + public void onFailure() { + mResult.completeExceptionally(new RuntimeException("Failed")); + } + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + + PrintWriter pw = getOutPrintWriter(); + try { + switch (cmd) { + case "add-role-holder": + return runAddRoleHolder(); + case "remove-role-holder": + return runRemoveRoleHolder(); + case "clear-role-holders": + return runClearRoleHolders(); + default: + return handleDefaultCommands(cmd); + } + } catch (RemoteException e) { + pw.println("Remote exception: " + e); + } + return -1; + } + + private int getUserIdMaybe() { + int userId = UserHandle.USER_SYSTEM; + String option = getNextOption(); + if (option != null && option.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } + return userId; + } + + private int runAddRoleHolder() throws RemoteException { + int userId = getUserIdMaybe(); + String roleName = getNextArgRequired(); + String packageName = getNextArgRequired(); + + Callback callback = new Callback(); + mRoleManager.addRoleHolderAsUser(roleName, packageName, userId, callback); + return callback.waitForResult(); + } + + private int runRemoveRoleHolder() throws RemoteException { + int userId = getUserIdMaybe(); + String roleName = getNextArgRequired(); + String packageName = getNextArgRequired(); + + Callback callback = new Callback(); + mRoleManager.removeRoleHolderAsUser(roleName, packageName, userId, callback); + return callback.waitForResult(); + } + + private int runClearRoleHolders() throws RemoteException { + int userId = getUserIdMaybe(); + String roleName = getNextArgRequired(); + + Callback callback = new Callback(); + mRoleManager.clearRoleHoldersAsUser(roleName, userId, callback); + return callback.waitForResult(); + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + pw.println("Role manager (role) commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(); + pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE"); + pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE"); + pw.println(" clear-role-holders [--user USER_ID] ROLE"); + pw.println(); + } +} diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java index 33584d4a1710..84750b385d97 100644 --- a/services/core/java/com/android/server/wm/ActivityDisplay.java +++ b/services/core/java/com/android/server/wm/ActivityDisplay.java @@ -181,6 +181,11 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> mWindowContainerController.onDisplayChanged(); } + @Override + public void onInitializeOverrideConfiguration(Configuration config) { + getOverrideConfiguration().updateFrom(config); + } + void addChild(ActivityStack stack, int position) { if (position == POSITION_BOTTOM) { position = 0; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 8f99dae89316..11e998c7ec89 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -4511,6 +4511,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mKeyguardController.isKeyguardLocked(); } + /** + * Clears launch params for the given package. + * @param packageNames the names of the packages of which the launch params are to be cleared + */ + @Override + public void clearLaunchParamsForPackages(List<String> packageNames) { + mAmInternal.enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, + "clearLaunchParamsForPackages"); + synchronized (mGlobalLock) { + for (int i = 0; i < packageNames.size(); ++i) { + mStackSupervisor.mLaunchParamsPersister.removeRecordForPackage(packageNames.get(i)); + } + } + } + void dumpLastANRLocked(PrintWriter pw) { pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)"); if (mLastANRState == null) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a834ef16f426..c0e983653b27 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -34,6 +34,7 @@ import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.GONE; +import static android.view.InsetsState.TYPE_IME; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_TOP; @@ -123,6 +124,7 @@ import android.animation.AnimationHandler; import android.annotation.CallSuper; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -150,6 +152,7 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.InputChannel; import android.view.InputDevice; +import android.view.InsetsState.InternalInsetType; import android.view.MagnificationSpec; import android.view.Surface; import android.view.SurfaceControl; @@ -161,6 +164,7 @@ import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; +import com.android.internal.util.function.TriConsumer; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.utils.DisplayRotationUtil; import com.android.server.wm.utils.RotationCache; @@ -506,6 +510,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final PointerEventDispatcher mPointerEventDispatcher; + private final InsetsStateController mInsetsStateController; + // Last systemUiVisibility we received from status bar. private int mLastStatusBarVisibility = 0; // Last systemUiVisibility we dispatched to windows. @@ -902,6 +908,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mService.mAnimator.addDisplayLocked(mDisplayId); mInputMonitor = new InputMonitor(service, mDisplayId); + mInsetsStateController = new InsetsStateController(this); } boolean isReady() { @@ -1038,6 +1045,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mDisplayRotation; } + /** + * Marks a window as providing insets for the rest of the windows in the system. + * + * @param type The type of inset this window provides. + * @param win The window. + * @param frameProvider Function to compute the frame, or {@code null} if the just the frame of + * the window should be taken. + */ + void setInsetProvider(@InternalInsetType int type, WindowState win, + @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) { + mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider); + } + + InsetsStateController getInsetsStateController() { + return mInsetsStateController; + } + @VisibleForTesting void setDisplayRotation(DisplayRotation displayRotation) { mDisplayRotation = displayRotation; @@ -2712,6 +2736,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplayRotation.dump(prefix, pw); pw.println(); mInputMonitor.dump(pw, " "); + pw.println(); + mInsetsStateController.dump(prefix, pw); } @Override @@ -2994,6 +3020,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInputMethodWindow.getDisplayId()); } computeImeTarget(true /* updateImeTarget */); + mInsetsStateController.getSourceProvider(TYPE_IME).setWindow(win, + null /* frameProvider */); } /** @@ -3449,6 +3477,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw(); if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats( "after finishPostLayoutPolicyLw", pendingLayoutChanges); + mInsetsStateController.onPostLayout(); } while (pendingLayoutChanges != 0); mTmpApplySurfaceChangesTransactionState.reset(); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index c16f95ee1160..0e5947af0c61 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; +import static android.view.InsetsState.TYPE_TOP_BAR; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; @@ -128,6 +129,7 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputEventReceiver; +import android.view.InsetsState; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.Surface; @@ -804,6 +806,11 @@ public class DisplayPolicy { if (mDisplayContent.isDefaultDisplay) { mService.mPolicy.setKeyguardCandidateLw(win); } + mDisplayContent.setInsetProvider(TYPE_TOP_BAR, win, + (displayFrames, windowState, rect) -> { + rect.top = 0; + rect.bottom = getStatusBarHeight(displayFrames); + }); break; case TYPE_NAVIGATION_BAR: mContext.enforceCallingOrSelfPermission( @@ -818,6 +825,8 @@ public class DisplayPolicy { mNavigationBarController.setWindow(win); mNavigationBarController.setOnBarVisibilityChangedListener( mNavBarVisibilityListener, true); + mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, + win, null /* frameProvider */); if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar); break; case TYPE_NAVIGATION_BAR_PANEL: @@ -845,9 +854,11 @@ public class DisplayPolicy { if (mDisplayContent.isDefaultDisplay) { mService.mPolicy.setKeyguardCandidateLw(null); } + mDisplayContent.setInsetProvider(TYPE_TOP_BAR, null, null); } else if (mNavigationBar == win) { mNavigationBar = null; mNavigationBarController.setWindow(null); + mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, null, null); } if (mLastFocusedWindow == win) { mLastFocusedWindow = null; @@ -855,6 +866,11 @@ public class DisplayPolicy { mScreenDecorWindows.remove(win); } + private int getStatusBarHeight(DisplayFrames displayFrames) { + return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation], + displayFrames.mDisplayCutoutSafe.top); + } + /** * Control the animation to run when a window's state changes. Return a * non-0 number to force the animation to a specific resource ID, or 0 diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index f1d1e49c1004..7aabc15d9860 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -21,6 +21,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; @@ -30,6 +31,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.power.V1_0.PowerHint; +import android.net.Uri; import android.os.Handler; import android.os.SystemProperties; import android.os.UserHandle; @@ -57,6 +59,7 @@ public class DisplayRotation { private final WindowManagerService mService; private final DisplayContent mDisplayContent; private final DisplayPolicy mDisplayPolicy; + private final DisplayWindowSettings mDisplayWindowSettings; private final Context mContext; private final Object mLock; @@ -71,10 +74,6 @@ public class DisplayRotation { private StatusBarManagerInternal mStatusBarManagerInternal; private SettingsObserver mSettingsObserver; - // Default display does not rotate, apps that require non-default orientation will have to - // have the orientation emulated. - private boolean mForceDefaultOrientation; - private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @VisibleForTesting @@ -93,6 +92,13 @@ public class DisplayRotation { private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; private int mUserRotation = Surface.ROTATION_0; + /** + * A flag to indicate if the display rotation should be fixed to user specified rotation + * regardless of all other states (including app requrested orientation). {@code true} the + * display rotation should be fixed to user specified rotation, {@code false} otherwise. + */ + private boolean mFixedToUserRotation; + private int mDemoHdmiRotation; private int mDemoRotation; private boolean mDemoHdmiRotationLock; @@ -100,15 +106,17 @@ public class DisplayRotation { DisplayRotation(WindowManagerService service, DisplayContent displayContent) { this(service, displayContent, displayContent.getDisplayPolicy(), - service.mContext, service.getWindowManagerLock()); + service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock()); } @VisibleForTesting DisplayRotation(WindowManagerService service, DisplayContent displayContent, - DisplayPolicy displayPolicy, Context context, Object lock) { + DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings, + Context context, Object lock) { mService = service; mDisplayContent = displayContent; mDisplayPolicy = displayPolicy; + mDisplayWindowSettings = displayWindowSettings; mContext = context; mLock = lock; isDefaultDisplay = displayContent.isDefaultDisplay; @@ -204,12 +212,19 @@ public class DisplayRotation { // so if the orientation is forced, we need to respect that no matter what. final boolean isTv = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); - mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) && - res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) && - // For debug purposes the next line turns this feature off with: - // $ adb shell setprop config.override_forced_orient true - // $ adb shell wm size reset - !"true".equals(SystemProperties.get("config.override_forced_orient")); + final boolean forceDefaultOrientationInRes = + res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation); + final boolean forceDefaultOrienation = + ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) + && forceDefaultOrientationInRes + // For debug purposes the next line turns this feature off with: + // $ adb shell setprop config.override_forced_orient true + // $ adb shell wm size reset + && !"true".equals(SystemProperties.get("config.override_forced_orient")); + // Configuration says we force to use the default orientation. We can fall back to fix + // rotation to only user rotation. As long as OEM doesn't change user rotation then the + // rotation of this display is effectively stuck at 0 deg. + setFixedToUserRotation(forceDefaultOrienation); } void setRotation(int rotation) { @@ -227,7 +242,14 @@ public class DisplayRotation { } } - void restoreUserRotation(int userRotationMode, int userRotation) { + void restoreSettings(int userRotationMode, int userRotation, + boolean fixedToUserRotation) { + mFixedToUserRotation = fixedToUserRotation; + + // We will retrieve user rotation and user rotation mode from settings for default display. + if (isDefaultDisplay) { + return; + } if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) { Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode @@ -243,6 +265,18 @@ public class DisplayRotation { mUserRotation = userRotation; } + void setFixedToUserRotation(boolean fixedToUserRotation) { + if (mFixedToUserRotation == fixedToUserRotation) { + return; + } + + mFixedToUserRotation = fixedToUserRotation; + mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, + fixedToUserRotation); + mService.updateRotation(true /* alwaysSendConfiguration */, + false /* forceRelayout */); + } + private void setUserRotation(int userRotationMode, int userRotation) { if (isDefaultDisplay) { // We'll be notified via settings listener, so we don't need to update internal values. @@ -265,7 +299,7 @@ public class DisplayRotation { mUserRotation = userRotation; changed = true; } - mService.mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode, + mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode, userRotation); if (changed) { mService.updateRotation(true /* alwaysSendConfiguration */, @@ -291,9 +325,8 @@ public class DisplayRotation { Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0; } - /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */ - boolean isDefaultOrientationForced() { - return mForceDefaultOrientation; + boolean isFixedToUserRotation() { + return mFixedToUserRotation; } public int getLandscapeRotation() { @@ -399,6 +432,12 @@ public class DisplayRotation { * screen is switched off. */ private boolean needSensorRunning() { + if (mFixedToUserRotation) { + // We are sure we only respect user rotation settings, so we are sure we will not + // support sensor rotation. + return false; + } + if (mSupportAutoRotation) { if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR @@ -459,8 +498,8 @@ public class DisplayRotation { ); } - if (mForceDefaultOrientation) { - return Surface.ROTATION_0; + if (mFixedToUserRotation) { + return mUserRotation; } int sensorRotation = mOrientationListener != null @@ -701,8 +740,8 @@ public class DisplayRotation { // demo, hdmi, vr, etc mode. // Determine if the rotation is currently forced. - if (mForceDefaultOrientation) { - return false; // Rotation is forced to default orientation. + if (mFixedToUserRotation) { + return false; // Rotation is forced to user settings. } final int lidState = mDisplayPolicy.getLidState(); @@ -861,6 +900,7 @@ public class DisplayRotation { pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock); pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation)); pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation)); + pw.println(prefix + " mFixedToUserRotation=" + mFixedToUserRotation); } private class OrientationListener extends WindowOrientationListener { @@ -945,4 +985,10 @@ public class DisplayRotation { } } } + + @VisibleForTesting + interface ContentObserverRegister { + void registerContentObserver(Uri uri, boolean notifyForDescendants, + ContentObserver observer, @UserIdInt int userHandle); + } } diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index f7dfd3ffc8bf..45d77dee1851 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -80,6 +80,7 @@ class DisplayWindowSettings { private boolean mShouldShowWithInsecureKeyguard = false; private boolean mShouldShowSystemDecors = false; private boolean mShouldShowIme = false; + private boolean mFixedToUserRotation; private Entry(String name) { mName = name; @@ -97,7 +98,8 @@ class DisplayWindowSettings { && mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED && !mShouldShowWithInsecureKeyguard && !mShouldShowSystemDecors - && !mShouldShowIme; + && !mShouldShowIme + && !mFixedToUserRotation; } } @@ -186,6 +188,13 @@ class DisplayWindowSettings { writeSettingsIfNeeded(entry, displayInfo); } + void setFixedToUserRotation(DisplayContent displayContent, boolean fixedToUserRotation) { + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final Entry entry = getOrCreateEntry(displayInfo); + entry.mFixedToUserRotation = fixedToUserRotation; + writeSettingsIfNeeded(entry, displayInfo); + } + private int getWindowingModeLocked(Entry entry, int displayId) { int windowingMode = entry != null ? entry.mWindowingMode : WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -331,7 +340,8 @@ class DisplayWindowSettings { displayInfo.overscanRight = entry.mOverscanRight; displayInfo.overscanBottom = entry.mOverscanBottom; - dc.getDisplayRotation().restoreUserRotation(entry.mUserRotationMode, entry.mUserRotation); + dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode, + entry.mUserRotation, entry.mFixedToUserRotation); if (entry.mForcedDensity != 0) { dc.mBaseDisplayDensity = entry.mForcedDensity; @@ -458,6 +468,8 @@ class DisplayWindowSettings { "shouldShowWithInsecureKeyguard"); entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors"); entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme"); + entry.mFixedToUserRotation = getBooleanAttribute(parser, + "fixedToUserRotation"); mEntries.put(name, entry); } XmlUtils.skipCurrentTag(parser); @@ -541,6 +553,10 @@ class DisplayWindowSettings { if (entry.mShouldShowIme) { out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme)); } + if (entry.mFixedToUserRotation) { + out.attribute(null, "fixedToUserRotation", + Boolean.toString(entry.mFixedToUserRotation)); + } out.endTag(null, "display"); } diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java new file mode 100644 index 000000000000..e96f0b1c4416 --- /dev/null +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -0,0 +1,87 @@ +/* + * 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; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Rect; +import android.view.InsetsSource; + +import com.android.internal.util.function.TriConsumer; +import com.android.server.policy.WindowManagerPolicy; + +/** + * Controller for a specific inset source on the server. It's called provider as it provides the + * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}. + */ +class InsetsSourceProvider { + + private final Rect mTmpRect = new Rect(); + private final @NonNull InsetsSource mSource; + private WindowState mWin; + private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider; + + InsetsSourceProvider(InsetsSource source) { + mSource = source; + } + + InsetsSource getSource() { + return mSource; + } + + /** + * Updates the window that currently backs this source. + * + * @param win The window that links to this source. + * @param frameProvider Based on display frame state and the window, calculates the resulting + * frame that should be reported to clients. + */ + void setWindow(@Nullable WindowState win, + @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) { + if (mWin != null) { + mWin.setInsetProvider(null); + } + mWin = win; + mFrameProvider = frameProvider; + if (win == null) { + mSource.setVisible(false); + mSource.setFrame(new Rect()); + } else { + mSource.setVisible(true); + mWin.setInsetProvider(this); + } + } + + /** + * Called when a layout pass has occurred. + */ + void onPostLayout() { + if (mWin == null) { + return; + } + + mTmpRect.set(mWin.getFrameLw()); + if (mFrameProvider != null) { + mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect); + } else { + mTmpRect.inset(mWin.mGivenContentInsets); + } + mSource.setFrame(mTmpRect); + mSource.setVisible(mWin.isVisible() && !mWin.mGivenInsetsPending); + + } +} diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java new file mode 100644 index 000000000000..1189ee660605 --- /dev/null +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -0,0 +1,104 @@ +/* + * 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; + +import static android.view.InsetsState.TYPE_IME; +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static android.view.InsetsState.TYPE_TOP_BAR; + +import android.util.ArrayMap; +import android.view.InsetsState; + +import java.io.PrintWriter; +import java.util.function.Consumer; + +/** + * Manages global window inset state in the system represented by {@link InsetsState}. + */ +class InsetsStateController { + + private final InsetsState mLastState = new InsetsState(); + private final InsetsState mState = new InsetsState(); + private final DisplayContent mDisplayContent; + private ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>(); + + private final Consumer<WindowState> mDispatchInsetsChanged = w -> { + if (w.isVisible()) { + w.notifyInsetsChanged(); + } + }; + + InsetsStateController(DisplayContent displayContent) { + mDisplayContent = displayContent; + } + + /** + * When dispatching window state to the client, we'll need to exclude the source that represents + * the window that is being dispatched. + * + * @param target The client we dispatch the state to. + * @return The state stripped of the necessary information. + */ + InsetsState getInsetsForDispatch(WindowState target) { + final InsetsSourceProvider provider = target.getInsetProvider(); + if (provider == null) { + return mState; + } + + final InsetsState state = new InsetsState(); + state.set(mState); + final int type = provider.getSource().getType(); + state.removeSource(type); + + // Navigation bar doesn't get influenced by anything else + if (type == TYPE_NAVIGATION_BAR) { + state.removeSource(TYPE_IME); + state.removeSource(TYPE_TOP_BAR); + } + return state; + } + + /** + * @return The provider of a specific type. + */ + InsetsSourceProvider getSourceProvider(int type) { + return mControllers.computeIfAbsent(type, + key -> new InsetsSourceProvider(mState.getSource(key))); + } + + /** + * Called when a layout pass has occurred. + */ + void onPostLayout() { + for (int i = mControllers.size() - 1; i>= 0; i--) { + mControllers.valueAt(i).onPostLayout(); + } + if (!mLastState.equals(mState)) { + mLastState.set(mState, true /* copySources */); + notifyInsetsChanged(); + } + } + + private void notifyInsetsChanged() { + mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */); + } + + void dump(String prefix, PrintWriter pw) { + pw.println(prefix + "WindowInsetsStateController"); + mState.dump(prefix + " ", pw); + } +} diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index da9a5071b100..bc6a690b6e74 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -269,7 +269,7 @@ class LaunchParamsPersister { outParams.mBounds.set(persistableParams.mBounds); } - private void onPackageRemoved(String packageName) { + void removeRecordForPackage(String packageName) { final List<File> fileToDelete = new ArrayList<>(); for (int i = 0; i < mMap.size(); ++i) { int userId = mMap.keyAt(i); @@ -310,7 +310,7 @@ class LaunchParamsPersister { @Override public void onPackageRemoved(String packageName) { - LaunchParamsPersister.this.onPackageRemoved(packageName); + removeRecordForPackage(packageName); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index c2bc677f5e39..80d1368427a3 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -233,6 +233,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final DisplayContent existing = getDisplayContent(displayId); if (existing != null) { + initializeDisplayOverrideConfiguration(controller, existing); existing.setController(controller); return existing; } @@ -242,6 +243,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display); mService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc); + initializeDisplayOverrideConfiguration(controller, dc); if (mService.mDisplayManagerInternal != null) { mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager( @@ -254,6 +256,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return dc; } + /** + * The display content may have configuration set from {@link #DisplayWindowSettings}. This + * callback let the owner of container know there is existing configuration to prevent the + * values from being replaced by the initializing {@link #ActivityDisplay}. + */ + private void initializeDisplayOverrideConfiguration(DisplayWindowController controller, + DisplayContent displayContent) { + if (controller != null && controller.mListener != null) { + controller.mListener.onInitializeOverrideConfiguration( + displayContent.getOverrideConfiguration()); + } + } + boolean isLayoutNeeded() { final int numDisplays = mChildren.size(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index ee0e89b24769..3947bd47b588 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -222,7 +222,7 @@ class ScreenRotationAnimation { } public ScreenRotationAnimation(Context context, DisplayContent displayContent, - boolean forceDefaultOrientation, boolean isSecure, WindowManagerService service) { + boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) { mService = service; mContext = context; mDisplayContent = displayContent; @@ -234,7 +234,7 @@ class ScreenRotationAnimation { final int originalWidth; final int originalHeight; DisplayInfo displayInfo = displayContent.getDisplayInfo(); - if (forceDefaultOrientation) { + if (fixedToUserRotation) { // Emulated orientation. mForceDefaultOrientation = true; originalWidth = displayContent.mBaseDisplayWidth; diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 6838c55100e8..37b5a7c30218 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -50,6 +50,7 @@ import android.view.InputChannel; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceSession; +import android.view.InsetsState; import android.view.WindowManager; import com.android.internal.os.logging.MetricsLoggerWrapper; @@ -153,17 +154,21 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, - DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) { + DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, + InsetsState outInsetsState) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame, - outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel); + outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel, + outInsetsState); } @Override public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs, - int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) { + int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, + InsetsState outInsetsState) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */, - new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */); + new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */, + outInsetsState); } @Override @@ -182,7 +187,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame, DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration, - Surface outSurface) { + Surface outSurface, InsetsState outInsetsState) { if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag); @@ -190,7 +195,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { requestedWidth, requestedHeight, viewFlags, flags, frameNumber, outFrame, outOverscanInsets, outContentInsets, outVisibleInsets, outStableInsets, outsets, outBackdropFrame, cutout, - mergedConfiguration, outSurface); + mergedConfiguration, outSurface, outInsetsState); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 8f02f8aa5dd9..9a56606aee7f 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -66,6 +66,7 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; import android.view.ViewGroup.LayoutParams; +import android.view.InsetsState; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -142,6 +143,7 @@ class TaskSnapshotSurface implements StartingSurface { final Rect taskBounds; final Rect tmpContentInsets = new Rect(); final Rect tmpStableInsets = new Rect(); + final InsetsState mTmpInsetsState = new InsetsState(); final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); int backgroundColor = WHITE; int statusBarColor = 0; @@ -202,7 +204,7 @@ class TaskSnapshotSurface implements StartingSurface { try { final int res = session.addToDisplay(window, window.mSeq, layoutParams, View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect, - tmpRect, tmpCutout, null); + tmpRect, tmpCutout, null, mTmpInsetsState); if (res < 0) { Slog.w(TAG, "Failed to add snapshot starting window res=" + res); return null; @@ -218,7 +220,7 @@ class TaskSnapshotSurface implements StartingSurface { try { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1, tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect, - tmpCutout, tmpMergedConfiguration, surface); + tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState); } catch (RemoteException e) { // Local call. } diff --git a/services/core/java/com/android/server/wm/WindowContainerListener.java b/services/core/java/com/android/server/wm/WindowContainerListener.java index 4b3cd36040c6..3d3d2e02693c 100644 --- a/services/core/java/com/android/server/wm/WindowContainerListener.java +++ b/services/core/java/com/android/server/wm/WindowContainerListener.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import android.content.res.Configuration; + /** * Interface used by the owner/creator of the container to listen to changes with the container. * @see WindowContainerController @@ -23,4 +25,5 @@ package com.android.server.wm; public interface WindowContainerListener { void registerConfigurationChangeListener(ConfigurationContainerListener listener); void unregisterConfigurationChangeListener(ConfigurationContainerListener listener); + default void onInitializeOverrideConfiguration(Configuration config) {} } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5df34517956a..4085f3d8062a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -218,6 +218,7 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; import android.view.WindowContentFrameStats; +import android.view.InsetsState; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.RemoveContentMode; @@ -1111,7 +1112,8 @@ public class WindowManagerService extends IWindowManager.Stub public int addWindow(Session session, IWindow client, int seq, LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, - DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) { + DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, + InsetsState outInsetsState) { int[] appOp = new int[1]; int res = mPolicy.checkAddPermission(attrs, appOp); if (res != WindowManagerGlobal.ADD_OKAY) { @@ -1459,6 +1461,7 @@ public class WindowManagerService extends IWindowManager.Stub outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) { res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR; } + outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win)); if (mInTouchMode) { res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; @@ -1856,7 +1859,7 @@ public class WindowManagerService extends IWindowManager.Stub long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame, DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration, - Surface outSurface) { + Surface outSurface, InsetsState outInsetsState) { int result = 0; boolean configChanged; final boolean hasStatusBarPermission = @@ -2157,6 +2160,7 @@ public class WindowManagerService extends IWindowManager.Stub outStableInsets, outOutsets); outCutout.set(win.getWmDisplayCutout().getDisplayCutout()); outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw())); + outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win)); if (localLOGV) Slog.v( TAG_WM, "Relayout given client " + client.asBinder() + ", requestedWidth=" + requestedWidth @@ -3587,6 +3591,17 @@ public class WindowManagerService extends IWindowManager.Stub } } + void setRotateForApp(int displayId, boolean enabled) { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to set rotate for app for a missing display."); + return; + } + display.getDisplayRotation().setFixedToUserRotation(enabled); + } + } + @Override public void freezeRotation(int rotation) { freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation); @@ -5393,7 +5408,7 @@ public class WindowManagerService extends IWindowManager.Stub displayContent.updateDisplayInfo(); screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent, - displayContent.getDisplayRotation().isDefaultOrientationForced(), isSecure, + displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure, this); mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId, screenRotationAnimation); diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index bf77ba86075d..6865ce305442 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -76,6 +76,8 @@ public class WindowManagerShellCommand extends ShellCommand { getNextArgRequired()); case "set-user-rotation": return runSetDisplayUserRotation(pw); + case "set-fix-to-user-rotation": + return runSetFixToUserRotation(pw); default: return handleDefaultCommands(cmd); } @@ -297,6 +299,32 @@ public class WindowManagerShellCommand extends ShellCommand { } } + private int runSetFixToUserRotation(PrintWriter pw) { + int displayId = Display.DEFAULT_DISPLAY; + String arg = getNextArgRequired(); + if ("-d".equals(arg)) { + displayId = Integer.parseInt(getNextArgRequired()); + arg = getNextArgRequired(); + } + + final boolean enabled; + switch (arg) { + case "enabled": + enabled = true; + break; + case "disabled": + enabled = false; + break; + default: + getErrPrintWriter().println("Error: expecting enabled or disabled, but we get " + + arg); + return -1; + } + + mInternal.setRotateForApp(displayId, enabled); + return 0; + } + @Override public void onHelp() { PrintWriter pw = getOutPrintWriter(); @@ -316,6 +344,8 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" Dismiss the keyguard, prompting user for auth if necessary."); pw.println(" set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]"); pw.println(" Set user rotation mode and user rotation."); + pw.println(" set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled]"); + pw.println(" Enable or disable rotating display for app requested orientation."); if (!IS_USER) { pw.println(" tracing (start | stop)"); pw.println(" Start or stop window tracing."); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 91c328809adb..cfd1f86cdeaa 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -143,6 +143,7 @@ import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES; import android.annotation.CallSuper; +import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.content.res.Configuration; @@ -194,6 +195,7 @@ import android.view.animation.Interpolator; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.policy.WindowManagerPolicy.DisplayContentInfo; import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; import com.android.server.wm.utils.InsetUtils; import com.android.server.wm.utils.WmDisplayCutout; @@ -578,6 +580,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private boolean mIsDimming = false; + private @Nullable InsetsSourceProvider mInsetProvider; + private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation, @@ -2955,6 +2959,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } + /** + * Called when the insets state changed. + */ + void notifyInsetsChanged() { + try { + mClient.insetsChanged( + getDisplayContent().getInsetsStateController().getInsetsForDispatch(this)); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver inset state change", e); + } + } + Rect getBackdropFrame(Rect frame) { // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing // start even if we haven't received the relayout window, so that the client requests @@ -4776,6 +4792,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWindowFrames.setContentChanged(false); } + void setInsetProvider(InsetsSourceProvider insetProvider) { + mInsetProvider = insetProvider; + } + + InsetsSourceProvider getInsetProvider() { + return mInsetProvider; + } + private final class MoveAnimationSpec implements AnimationSpec { private final long mDuration; diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 92efc3cc2b3c..99b827c11853 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.net; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; @@ -72,7 +72,6 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; @@ -142,9 +141,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent; -import com.android.server.net.NetworkPolicyManagerInternal; -import com.android.server.net.NetworkPolicyManagerService; -import com.android.server.net.NetworkStatsManagerInternal; +import com.android.server.DeviceIdleController; +import com.android.server.LocalServices; import com.google.common.util.concurrent.AbstractFuture; @@ -195,15 +193,6 @@ import java.util.stream.Collectors; /** * Tests for {@link NetworkPolicyManagerService}. - * - * <p>Typical usage: - * - * <pre><code> - m -j32 FrameworksServicesTests && adb install -r -g \ - ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \ - adb shell am instrument -e class "com.android.server.NetworkPolicyManagerServiceTest" -w \ - "com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner" - * </code></pre> */ @RunWith(AndroidJUnit4.class) @MediumTest @@ -376,7 +365,7 @@ public class NetworkPolicyManagerServiceTest { return null; } }).when(mActivityManager).registerUidObserver(any(), anyInt(), - eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), isNull(String.class)); + eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), any(String.class)); mFutureIntent = newRestrictBackgroundChangedFuture(); mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, @@ -425,7 +414,7 @@ public class NetworkPolicyManagerServiceTest { // catch INetworkManagementEventObserver during systemReady() final ArgumentCaptor<INetworkManagementEventObserver> networkObserver = - ArgumentCaptor.forClass(INetworkManagementEventObserver.class); + ArgumentCaptor.forClass(INetworkManagementEventObserver.class); verify(mNetworkManager).registerObserver(networkObserver.capture()); mNetworkObserver = networkObserver.getValue(); @@ -1782,7 +1771,7 @@ public class NetworkPolicyManagerServiceTest { } private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes, - long limitBytes, boolean inferred){ + long limitBytes, boolean inferred) { final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID); return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred); @@ -1880,7 +1869,7 @@ public class NetworkPolicyManagerServiceTest { } private static void assertNotificationType(int expected, String actualTag) { - assertEquals("notification type mismatch for '" + actualTag +"'", + assertEquals("notification type mismatch for '" + actualTag + "'", Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1)); } @@ -1914,7 +1903,8 @@ public class NetworkPolicyManagerServiceTest { final String action = ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; final Intent intent = future.get(5, TimeUnit.SECONDS); assertNotNull("Didn't get a " + action + "intent in 5 seconds"); - assertEquals("Wrong package on " + action + " intent", expectedPackage, intent.getPackage()); + assertEquals("Wrong package on " + action + " intent", + expectedPackage, intent.getPackage()); } // TODO: replace by Truth, Hamcrest, or a similar tool. @@ -1935,7 +1925,7 @@ public class NetworkPolicyManagerServiceTest { } if (errors.length() > 0) { fail("assertContainsInAnyOrder(expected=" + Arrays.toString(expected) - + ", actual=" + Arrays.toString(actual) +") failed: \n" + errors); + + ", actual=" + Arrays.toString(actual) + ") failed: \n" + errors); } } @@ -1998,7 +1988,7 @@ public class NetworkPolicyManagerServiceTest { @Override public Void answer(InvocationOnMock invocation) throws Throwable { - Log.d(TAG,"counting down on answer: " + invocation); + Log.d(TAG, "counting down on answer: " + invocation); latch.countDown(); return null; } @@ -2036,8 +2026,8 @@ public class NetworkPolicyManagerServiceTest { final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml; final File netConfigFile = new File(mPolicyDir, "netpolicy.xml"); Log.d(TAG, "Creating " + netConfigFile + " from asset " + assetPath); - try (final InputStream in = context.getResources().getAssets().open(assetPath); - final OutputStream out = new FileOutputStream(netConfigFile)) { + try (InputStream in = context.getResources().getAssets().open(assetPath); + OutputStream out = new FileOutputStream(netConfigFile)) { Streams.copy(in, out); } } @@ -2049,9 +2039,7 @@ public class NetworkPolicyManagerServiceTest { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NetPolicyXml { - - public String value() default ""; - + String value() default ""; } /** diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index d7988656c9de..ce59e6ec9760 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -31,6 +31,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.Signature; +import android.content.pm.UsesPermissionInfo; import android.os.Bundle; import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -464,6 +465,7 @@ public class PackageParserTest { pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo())); pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo())); pkg.requestedPermissions.add("foo7"); + pkg.usesPermissionInfos.add(new UsesPermissionInfo("foo7")); pkg.implicitPermissions.add("foo25"); pkg.protectedBroadcasts = new ArrayList<>(); diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java index b823e706a586..7f390a55232d 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -27,7 +27,12 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.WindowConfiguration; import android.platform.test.annotations.Presubmit; @@ -378,6 +383,33 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { mSecondaryDisplay.getDisplayRotation().getUserRotation()); } + @Test + public void testNotFixedToUserRotationByDefault() { + mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, + Surface.ROTATION_0); + + final DisplayRotation displayRotation = mock(DisplayRotation.class); + mPrimaryDisplay = spy(mPrimaryDisplay); + when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation); + + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false)); + } + + @Test + public void testSetFixedToUserRotation() { + mTarget.setFixedToUserRotation(mPrimaryDisplay, true); + + final DisplayRotation displayRotation = mock(DisplayRotation.class); + mPrimaryDisplay = spy(mPrimaryDisplay); + when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation); + + applySettingsToDisplayByNewInstance(mPrimaryDisplay); + + verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true)); + } + private static void assertOverscan(DisplayContent display, int left, int top, int right, int bottom) { final DisplayInfo info = display.getDisplayInfo(); diff --git a/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java new file mode 100644 index 000000000000..241b987e58ba --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -0,0 +1,78 @@ +/* + * 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; + +import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static org.junit.Assert.assertEquals; + +import android.graphics.Insets; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.InsetsSource; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +@RunWith(AndroidJUnit4.class) +@SmallTest +@FlakyTest(detail = "Promote once confirmed non-flaky") +@Presubmit +public class InsetsSourceProviderTest extends WindowTestsBase { + + private InsetsSourceProvider mProvider = new InsetsSourceProvider( + new InsetsSource(TYPE_TOP_BAR)); + + @Test + public void testPostLayout() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + topBar.getFrameLw().set(0, 0, 500, 100); + topBar.mHasSurface = true; + mProvider.setWindow(topBar, null); + mProvider.onPostLayout(); + assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame()); + assertEquals(Insets.of(0, 100, 0, 0), + mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */)); + } + + @Test + public void testPostLayout_invisible() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + topBar.getFrameLw().set(0, 0, 500, 100); + mProvider.setWindow(topBar, null); + mProvider.onPostLayout(); + assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */)); + } + + @Test + public void testPostLayout_frameProvider() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + topBar.getFrameLw().set(0, 0, 500, 100); + mProvider.setWindow(topBar, + (displayFrames, windowState, rect) -> { + rect.set(10, 10, 20, 20); + }); + mProvider.onPostLayout(); + assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java new file mode 100644 index 000000000000..7505db103bbf --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -0,0 +1,79 @@ +/* + * 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; + +import static android.view.InsetsState.TYPE_IME; +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.platform.test.annotations.Presubmit; +import android.view.InsetsState; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +@RunWith(AndroidJUnit4.class) +@SmallTest +@FlakyTest(detail = "Promote once confirmed non-flaky") +@Presubmit +public class InsetsStateControllerTest extends WindowTestsBase { + + @Test + public void testStripForDispatch_notOwn() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow"); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) + .setWindow(topBar, null); + topBar.setInsetProvider( + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)); + assertNotNull(mDisplayContent.getInsetsStateController().getInsetsForDispatch(app) + .getSource(TYPE_TOP_BAR)); + } + + @Test + public void testStripForDispatch_own() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) + .setWindow(topBar, null); + topBar.setInsetProvider( + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)); + assertEquals(new InsetsState(), + mDisplayContent.getInsetsStateController().getInsetsForDispatch(topBar)); + } + + @Test + public void testStripForDispatch_navBar() { + final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow"); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) + .setWindow(topBar, null); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_NAVIGATION_BAR) + .setWindow(navBar, null); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_IME) + .setWindow(ime, null); + assertEquals(new InsetsState(), + mDisplayContent.getInsetsStateController().getInsetsForDispatch(navBar)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java index 99deeb9e9397..432af0d7a469 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java @@ -24,6 +24,7 @@ import android.util.MergedConfiguration; import android.view.DisplayCutout; import android.view.DragEvent; import android.view.IWindow; +import android.view.InsetsState; import com.android.internal.os.IResultReceiver; @@ -39,6 +40,9 @@ public class TestIWindow extends IWindow.Stub { Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId, DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException { } + @Override + public void insetsChanged(InsetsState insetsState) throws RemoteException { + } @Override public void moved(int newX, int newY) throws RemoteException { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 38d8e3990e00..6c7ede3df4db 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -41,6 +41,7 @@ import static org.mockito.Mockito.when; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.AutomaticZenRule; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.ComponentName; @@ -1097,6 +1098,25 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertFalse(Objects.equals(defaultRuleName, ruleAfterUpdating.name)); // update name } + @Test + public void testAddAutomaticZenRule() { + AutomaticZenRule zenRule = new AutomaticZenRule("name", + new ComponentName("android", "ScheduleConditionProvider"), + ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + + assertTrue(id != null); + ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id); + assertTrue(ruleInConfig != null); + assertEquals(zenRule.isEnabled(), ruleInConfig.enabled); + assertEquals(zenRule.isModified(), ruleInConfig.modified); + assertEquals(zenRule.getConditionId(), ruleInConfig.conditionId); + assertEquals(NotificationManager.zenModeFromInterruptionFilter( + zenRule.getInterruptionFilter(), -1), ruleInConfig.zenMode); + assertEquals(zenRule.getName(), ruleInConfig.name); + } + private void setupZenConfig() { mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; mZenModeHelperSpy.mConfig.allowAlarms = false; diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index f128b4e2de2b..67acec689244 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -37,6 +37,8 @@ <application android:debuggable="true" android:testOnly="true"> + <uses-library android:name="android.test.mock" android:required="true" /> + <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" /> <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" /> <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" /> diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java new file mode 100644 index 000000000000..e9889948c341 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -0,0 +1,823 @@ +/* + * 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; + +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atMost; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.same; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.PowerManagerInternal; +import android.os.SystemClock; +import android.platform.test.annotations.Presubmit; +import android.provider.Settings; +import android.view.Surface; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.LocalServices; +import com.android.server.UiThread; +import com.android.server.policy.WindowManagerPolicy; +import com.android.server.statusbar.StatusBarManagerInternal; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Test class for {@link DisplayRotation}. + * + * Build/Install/Run: + * atest WmTests:DisplayRotationTests + */ +@SmallTest +@Presubmit +@FlakyTest(detail = "Confirm stable in post-submit before removing") +public class DisplayRotationTests { + private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50; + + private StatusBarManagerInternal mPreviousStatusBarManagerInternal; + + private WindowManagerService mMockWm; + private DisplayContent mMockDisplayContent; + private DisplayPolicy mMockDisplayPolicy; + private Context mMockContext; + private Resources mMockRes; + private SensorManager mMockSensorManager; + private Sensor mFakeSensor; + private DisplayWindowSettings mMockDisplayWindowSettings; + private ContentResolver mMockResolver; + private FakeSettingsProvider mFakeSettingsProvider; + private StatusBarManagerInternal mMockStatusBarManagerInternal; + + // Fields below are callbacks captured from test target. + private ContentObserver mShowRotationSuggestionsObserver; + private ContentObserver mAccelerometerRotationObserver; + private ContentObserver mUserRotationObserver; + private SensorEventListener mOrientationSensorListener; + + private DisplayRotationBuilder mBuilder; + + private DisplayRotation mTarget; + + @Before + public void setUp() { + FakeSettingsProvider.clearSettingsProvider(); + + mMockWm = mock(WindowManagerService.class); + mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); + + mPreviousStatusBarManagerInternal = LocalServices.getService( + StatusBarManagerInternal.class); + LocalServices.removeServiceForTest(StatusBarManagerInternal.class); + mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class); + LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal); + + mBuilder = new DisplayRotationBuilder(); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(StatusBarManagerInternal.class); + if (mPreviousStatusBarManagerInternal != null) { + LocalServices.addService(StatusBarManagerInternal.class, + mPreviousStatusBarManagerInternal); + mPreviousStatusBarManagerInternal = null; + } + } + + // ================================ + // Display Settings Related Tests + // ================================ + @Test + public void testLocksUserRotation_LockRotation_DefaultDisplay() throws Exception { + mBuilder.build(); + + freezeRotation(Surface.ROTATION_180); + + assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode()); + assertEquals(Surface.ROTATION_180, mTarget.getUserRotation()); + + assertEquals(0, Settings.System.getInt(mMockResolver, + Settings.System.ACCELEROMETER_ROTATION)); + assertEquals(Surface.ROTATION_180, Settings.System.getInt(mMockResolver, + Settings.System.USER_ROTATION)); + } + + @Test + public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception { + mBuilder.mIsDefaultDisplay = false; + + mBuilder.build(); + + freezeRotation(Surface.ROTATION_180); + + assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode()); + assertEquals(Surface.ROTATION_180, mTarget.getUserRotation()); + + verify(mMockDisplayWindowSettings).setUserRotation(mMockDisplayContent, + WindowManagerPolicy.USER_ROTATION_LOCKED, Surface.ROTATION_180); + } + + @Test + public void testPersistUserRotation_UnlockRotation_DefaultDisplay() throws Exception { + mBuilder.build(); + + thawRotation(); + + assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode()); + + assertEquals(1, Settings.System.getInt(mMockResolver, + Settings.System.ACCELEROMETER_ROTATION)); + } + + @Test + public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception { + mBuilder.mIsDefaultDisplay = false; + + mBuilder.build(); + + thawRotation(); + + assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode()); + + verify(mMockDisplayWindowSettings).setUserRotation(same(mMockDisplayContent), + eq(WindowManagerPolicy.USER_ROTATION_FREE), anyInt()); + } + + @Test + public void testPersistsFixedToUserRotation() throws Exception { + mBuilder.build(); + + mTarget.setFixedToUserRotation(true); + + verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, true); + + reset(mMockDisplayWindowSettings); + mTarget.setFixedToUserRotation(false); + + verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, false); + } + + // ======================================== + // Tests for User Rotation based Rotation + // ======================================== + @Test + public void testReturnsUserRotation_UserRotationLocked_NoAppRequest() + throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + freezeRotation(Surface.ROTATION_180); + + assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation( + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90)); + } + + @Test + public void testReturnsUserRotation_UserRotationLocked_CompatibleAppRequest() + throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + freezeRotation(Surface.ROTATION_180); + + assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation( + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90)); + } + + @Test + public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest() + throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + freezeRotation(Surface.ROTATION_180); + + final int rotation = mTarget.rotationForOrientation( + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_90); + assertTrue("Rotation should be sideways, but it's " + + Surface.rotationToString(rotation), + rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270); + } + + // ================================= + // Tests for Sensor based Rotation + // ================================= + private void verifyOrientationListenerRegistration(int numOfInvocation) { + final ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass( + SensorEventListener.class); + verify(mMockSensorManager, times(numOfInvocation)).registerListener( + listenerCaptor.capture(), + same(mFakeSensor), + anyInt(), + any()); + if (numOfInvocation > 0) { + mOrientationSensorListener = listenerCaptor.getValue(); + } + } + + @Test + public void testNotEnablesSensor_AutoRotationNotSupported() throws Exception { + mBuilder.setSupportAutoRotation(false).build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_ScreenNotOn() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(false); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_NotAwake() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(false); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_KeyguardNotDrawnCompletely() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(false); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_WindowManagerNotDrawnCompletely() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(false); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_FixedUserRotation() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.setFixedToUserRotation(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_ForceDefaultRotation() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + private void enableOrientationSensor() { + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(1); + } + + private SensorEvent createSensorEvent(int rotation) throws Exception { + final Constructor<SensorEvent> constructor = + SensorEvent.class.getDeclaredConstructor(int.class); + constructor.setAccessible(true); + final SensorEvent event = constructor.newInstance(1); + event.sensor = mFakeSensor; + event.values[0] = rotation; + event.timestamp = SystemClock.elapsedRealtimeNanos(); + return event; + } + + @Test + public void testReturnsSensorRotation_RotationThawed() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); + + assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + } + + private boolean waitForUiHandler() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + UiThread.getHandler().post(latch::countDown); + return latch.await(UI_HANDLER_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + @Test + public void testUpdatesRotationWhenSensorUpdates_RotationThawed() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); + assertTrue(waitForUiHandler()); + + verify(mMockWm).updateRotation(false, false); + } + + @Test + public void testNotifiesChoiceWhenSensorUpdates_RotationLocked() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + freezeRotation(Surface.ROTATION_270); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); + assertTrue(waitForUiHandler()); + + verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true); + } + + @Test + public void testReturnsCompatibleRotation_SensorEnabled_RotationThawed() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180)); + + final int rotation = mTarget.rotationForOrientation(SCREEN_ORIENTATION_LANDSCAPE, + Surface.ROTATION_0); + assertTrue("Rotation should be sideways but it's " + + Surface.rotationToString(rotation), + rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270); + } + + @Test + public void testReturnsUserRotation_SensorEnabled_RotationLocked() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + freezeRotation(Surface.ROTATION_270); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180)); + + assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + } + + // ================================= + // Tests for Policy based Rotation + // ================================= + @Test + public void testReturnsUserRotation_ForceDefaultRotation() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, + Surface.ROTATION_180)); + } + + @Test + public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false); + + assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, + Surface.ROTATION_180)); + } + + @Test + public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true); + + assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, + Surface.ROTATION_180)); + } + + @Test + public void testReturnsLidOpenRotation_LidOpen() throws Exception { + mBuilder.setLidOpenRotation(Surface.ROTATION_90).build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + when(mMockDisplayPolicy.getLidState()).thenReturn( + WindowManagerPolicy.WindowManagerFuncs.LID_OPEN); + + freezeRotation(Surface.ROTATION_270); + + assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + } + + @Test + public void testReturnsCarDockRotation_CarDockedMode() throws Exception { + mBuilder.setCarDockRotation(Surface.ROTATION_270).build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_CAR); + + freezeRotation(Surface.ROTATION_90); + + assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90)); + } + + @Test + public void testReturnsDeskDockRotation_DeskDockedMode() throws Exception { + mBuilder.setDeskDockRotation(Surface.ROTATION_270).build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK); + + freezeRotation(Surface.ROTATION_90); + + assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90)); + } + + @Test + public void testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest() + throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + mTarget.setFixedToUserRotation(true); + + freezeRotation(Surface.ROTATION_180); + + final int rotation = mTarget.rotationForOrientation( + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90); + assertEquals(Surface.ROTATION_180, rotation); + } + + @Test + public void testReturnsUserRotation_NonDefaultDisplay() throws Exception { + mBuilder.setIsDefaultDisplay(false).build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + freezeRotation(Surface.ROTATION_90); + + assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + } + + /** + * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget} + * according to given parameters. + */ + private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) { + final int width; + final int height; + switch (displayOrientation) { + case SCREEN_ORIENTATION_LANDSCAPE: + width = 1920; + height = 1080; + break; + case SCREEN_ORIENTATION_PORTRAIT: + width = 1080; + height = 1920; + break; + default: + throw new IllegalArgumentException("displayOrientation needs to be either landscape" + + " or portrait, but we got " + + ActivityInfo.screenOrientationToString(displayOrientation)); + } + + final PackageManager mockPackageManager = mock(PackageManager.class); + when(mMockContext.getPackageManager()).thenReturn(mockPackageManager); + when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) + .thenReturn(isCar); + when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) + .thenReturn(isTv); + + final int shortSizeDp = (isCar || isTv) ? 540 : 720; + final int longSizeDp = 960; + mTarget.configure(width, height, shortSizeDp, longSizeDp); + } + + private void freezeRotation(int rotation) { + mTarget.freezeRotation(rotation); + + if (mTarget.isDefaultDisplay) { + mAccelerometerRotationObserver.onChange(false); + mUserRotationObserver.onChange(false); + } + } + + private void thawRotation() { + mTarget.thawRotation(); + + if (mTarget.isDefaultDisplay) { + mAccelerometerRotationObserver.onChange(false); + mUserRotationObserver.onChange(false); + } + } + + private class DisplayRotationBuilder { + private boolean mIsDefaultDisplay = true; + private boolean mSupportAutoRotation = true; + + private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; + private int mCarDockRotation; + private int mDeskDockRotation; + private int mUndockedHdmiRotation; + + private DisplayRotationBuilder setIsDefaultDisplay(boolean isDefaultDisplay) { + mIsDefaultDisplay = isDefaultDisplay; + return this; + } + + private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) { + mSupportAutoRotation = supportAutoRotation; + return this; + } + + private DisplayRotationBuilder setLidOpenRotation(int rotation) { + mLidOpenRotation = rotation; + return this; + } + + private DisplayRotationBuilder setCarDockRotation(int rotation) { + mCarDockRotation = rotation; + return this; + } + + private DisplayRotationBuilder setDeskDockRotation(int rotation) { + mDeskDockRotation = rotation; + return this; + } + + private DisplayRotationBuilder setUndockedHdmiRotation(int rotation) { + mUndockedHdmiRotation = rotation; + return this; + } + + private void captureObservers() { + ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass( + ContentObserver.class); + verify(mMockResolver, atMost(1)).registerContentObserver( + eq(Settings.Secure.getUriFor(Settings.Secure.SHOW_ROTATION_SUGGESTIONS)), + anyBoolean(), + captor.capture(), + anyInt()); + if (!captor.getAllValues().isEmpty()) { + mShowRotationSuggestionsObserver = captor.getValue(); + } + + captor = ArgumentCaptor.forClass(ContentObserver.class); + verify(mMockResolver, atMost(1)).registerContentObserver( + eq(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION)), + anyBoolean(), + captor.capture(), + anyInt()); + if (!captor.getAllValues().isEmpty()) { + mAccelerometerRotationObserver = captor.getValue(); + } + + captor = ArgumentCaptor.forClass(ContentObserver.class); + verify(mMockResolver, atMost(1)).registerContentObserver( + eq(Settings.System.getUriFor(Settings.System.USER_ROTATION)), + anyBoolean(), + captor.capture(), + anyInt()); + if (!captor.getAllValues().isEmpty()) { + mUserRotationObserver = captor.getValue(); + } + } + + private Sensor createSensor(int type) throws Exception { + Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); + constr.setAccessible(true); + Sensor sensor = constr.newInstance(); + + setSensorType(sensor, type); + setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type); + setSensorField(sensor, "mVendor", "Mock Vendor"); + setSensorField(sensor, "mVersion", 1); + setSensorField(sensor, "mHandle", -1); + setSensorField(sensor, "mMaxRange", 10); + setSensorField(sensor, "mResolution", 1); + setSensorField(sensor, "mPower", 1); + setSensorField(sensor, "mMinDelay", 1000); + setSensorField(sensor, "mMaxDelay", 1000000000); + setSensorField(sensor, "mFlags", 0); + setSensorField(sensor, "mId", -1); + + return sensor; + } + + private void setSensorType(Sensor sensor, int type) throws Exception { + Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE); + setter.setAccessible(true); + setter.invoke(sensor, type); + } + + private void setSensorField(Sensor sensor, String fieldName, Object value) + throws Exception { + Field field = Sensor.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(sensor, value); + } + + private int convertRotationToDegrees(@Surface.Rotation int rotation) { + switch (rotation) { + case Surface.ROTATION_0: + return 0; + case Surface.ROTATION_90: + return 90; + case Surface.ROTATION_180: + return 180; + case Surface.ROTATION_270: + return 270; + default: + return -1; + } + } + + private void build() throws Exception { + mMockContext = mock(Context.class); + + mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class); + mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay; + + mMockDisplayPolicy = mock(DisplayPolicy.class); + + mMockRes = mock(Resources.class); + when(mMockContext.getResources()).thenReturn((mMockRes)); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation)) + .thenReturn(mSupportAutoRotation); + when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation)) + .thenReturn(convertRotationToDegrees(mLidOpenRotation)); + when(mMockRes.getInteger(com.android.internal.R.integer.config_carDockRotation)) + .thenReturn(convertRotationToDegrees(mCarDockRotation)); + when(mMockRes.getInteger(com.android.internal.R.integer.config_deskDockRotation)) + .thenReturn(convertRotationToDegrees(mDeskDockRotation)); + when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation)) + .thenReturn(convertRotationToDegrees(mUndockedHdmiRotation)); + + mMockSensorManager = mock(SensorManager.class); + when(mMockContext.getSystemService(Context.SENSOR_SERVICE)) + .thenReturn(mMockSensorManager); + mFakeSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION); + when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn( + Collections.singletonList(mFakeSensor)); + + mMockResolver = mock(ContentResolver.class); + when(mMockContext.getContentResolver()).thenReturn(mMockResolver); + mFakeSettingsProvider = new FakeSettingsProvider(); + when(mMockResolver.acquireProvider(Settings.AUTHORITY)) + .thenReturn(mFakeSettingsProvider.getIContentProvider()); + + mMockDisplayWindowSettings = mock(DisplayWindowSettings.class); + mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy, + mMockDisplayWindowSettings, mMockContext, new Object()); + + captureObservers(); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index 2c3c66be6248..f3a125bf79e4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -272,6 +272,51 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { } @Test + public void testClearsRecordInMemory() { + mTarget.saveTask(mTestTask); + + mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName()); + + mTarget.getLaunchParams(mTestTask, null, mResult); + + assertTrue("Result should be empty.", mResult.isEmpty()); + } + + @Test + public void testClearsWriteQueueItem() { + mTarget.saveTask(mTestTask); + + mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName()); + + final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor, + mUserFolderGetter); + target.onSystemReady(); + target.onUnlockUser(TEST_USER_ID); + + target.getLaunchParams(mTestTask, null, mResult); + + assertTrue("Result should be empty.", mResult.isEmpty()); + } + + @Test + public void testClearsFile() { + mTarget.saveTask(mTestTask); + mPersisterQueue.flush(); + + mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName()); + + final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor, + mUserFolderGetter); + target.onSystemReady(); + target.onUnlockUser(TEST_USER_ID); + + target.getLaunchParams(mTestTask, null, mResult); + + assertTrue("Result should be empty.", mResult.isEmpty()); + } + + + @Test public void testClearsRecordInMemoryOnPackageUninstalled() { mTarget.saveTask(mTestTask); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java index e56edabfa02f..3c877213a0e4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java @@ -65,7 +65,7 @@ public class WindowTestUtils { final DisplayRotation displayRotation = new DisplayRotation( mock(WindowManagerService.class), displayContent, displayPolicy, - context, new Object()); + mock(DisplayWindowSettings.class), context, new Object()); displayRotation.mPortraitRotation = Surface.ROTATION_0; displayRotation.mLandscapeRotation = Surface.ROTATION_90; displayRotation.mUpsideDownRotation = Surface.ROTATION_180; diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 34a8c9659595..45cfe1e303ec 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -569,14 +569,6 @@ public class SubscriptionManager { public static final String IS_OPPORTUNISTIC = "is_opportunistic"; /** - * TelephonyProvider column name for subId of parent subscription of an opportunistic - * subscription. - * if the parent sub id is valid, then is_opportunistic should always to true. - * @hide - */ - public static final String PARENT_SUB_ID = "parent_sub_id"; - - /** * TelephonyProvider column name for group ID. Subscriptions with same group ID * are considered bundled together, and should behave as a single subscription at * certain scenarios. diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java index ae3914ebf162..d5987a5373b4 100644 --- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java +++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java @@ -26,6 +26,7 @@ import android.util.MergedConfiguration; import android.view.Display; import android.view.DisplayCutout; import android.view.IWindowSession; +import android.view.InsetsState; import android.view.Surface; import android.view.View; import android.view.WindowManager; @@ -105,7 +106,7 @@ public class MainActivity extends Activity { window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(), - new Surface()); + new Surface(), new InsetsState()); } catch (RemoteException e) { e.printStackTrace(); } @@ -131,8 +132,9 @@ public class MainActivity extends Activity { final IWindowSession session = WindowManagerGlobal.getWindowSession(); final Rect tmpRect = new Rect(); try { - final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq, layoutParams, - View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect); + final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq, + layoutParams, View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect, + new InsetsState()); } catch (RemoteException e) { e.printStackTrace(); } diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 3ec8a4155292..364d5084fbc9 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -58,7 +58,7 @@ interface IWifiManager */ oneway void requestActivityInfo(in ResultReceiver result); - ParceledListSlice getConfiguredNetworks(); + ParceledListSlice getConfiguredNetworks(String packageName); ParceledListSlice getPrivilegedConfiguredNetworks(); @@ -90,11 +90,11 @@ interface IWifiManager List<ScanResult> getScanResults(String callingPackage); - void disconnect(String packageName); + boolean disconnect(String packageName); - void reconnect(String packageName); + boolean reconnect(String packageName); - void reassociate(String packageName); + boolean reassociate(String packageName); WifiInfo getConnectionInfo(String callingPackage); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 7aff03c00dd3..8dd6c771a924 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1067,7 +1067,7 @@ public class WifiManager { public List<WifiConfiguration> getConfiguredNetworks() { try { ParceledListSlice<WifiConfiguration> parceledList = - mService.getConfiguredNetworks(); + mService.getConfiguredNetworks(mContext.getOpPackageName()); if (parceledList == null) { return Collections.emptyList(); } @@ -1761,8 +1761,7 @@ public class WifiManager { @Deprecated public boolean disconnect() { try { - mService.disconnect(mContext.getOpPackageName()); - return true; + return mService.disconnect(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1786,8 +1785,7 @@ public class WifiManager { @Deprecated public boolean reconnect() { try { - mService.reconnect(mContext.getOpPackageName()); - return true; + return mService.reconnect(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1811,8 +1809,7 @@ public class WifiManager { @Deprecated public boolean reassociate() { try { - mService.reassociate(mContext.getOpPackageName()); - return true; + return mService.reassociate(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2132,14 +2129,14 @@ public class WifiManager { * existing networks. You should assume the network IDs can be different * after calling this method. * - * @return {@code false} Will always return true. + * @return {@code false}. * @deprecated There is no need to call this method - * {@link #addNetwork(WifiConfiguration)}, {@link #updateNetwork(WifiConfiguration)} * and {@link #removeNetwork(int)} already persist the configurations automatically. */ @Deprecated public boolean saveConfiguration() { - return true; + return false; } /** @@ -3406,6 +3403,11 @@ public class WifiManager { * @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void connect(WifiConfiguration config, ActionListener listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); // Use INVALID_NETWORK_ID for arg1 when passing a config object @@ -3426,7 +3428,12 @@ public class WifiManager { * initialized again * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void connect(int networkId, ActionListener listener) { if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative"); getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener)); @@ -3452,7 +3459,12 @@ public class WifiManager { * initialized again * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void save(WifiConfiguration config, ActionListener listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config); @@ -3471,7 +3483,12 @@ public class WifiManager { * initialized again * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void forget(int netId, ActionListener listener) { if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative"); getChannel().sendMessage(FORGET_NETWORK, netId, putListener(listener)); @@ -3486,7 +3503,12 @@ public class WifiManager { * initialized again * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void disable(int netId, ActionListener listener) { if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative"); getChannel().sendMessage(DISABLE_NETWORK, netId, putListener(listener)); @@ -3498,6 +3520,12 @@ public class WifiManager { * @param SSID, in the format of WifiConfiguration's SSID. * @hide */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void disableEphemeralNetwork(String SSID) { if (SSID == null) throw new IllegalArgumentException("SSID cannot be null"); try { diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 529548f1d2b8..6622a2571870 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -184,6 +184,9 @@ public class WifiScanner { public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings"; /** {@hide} */ public static final String SCAN_PARAMS_WORK_SOURCE_KEY = "WorkSource"; + /** {@hide} */ + public static final String REQUEST_PACKAGE_NAME_KEY = "PackageName"; + /** * scan configuration parameters to be sent to {@link #startBackgroundScan} */ @@ -798,6 +801,7 @@ public class WifiScanner { Bundle scanParams = new Bundle(); scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams); } @@ -812,8 +816,11 @@ public class WifiScanner { int key = removeListener(listener); if (key == INVALID_KEY) return; validateChannel(); - mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key); + Bundle scanParams = new Bundle(); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key, scanParams); } + /** * reports currently available scan results on appropriate listeners * @return true if all scan results were reported correctly @@ -821,7 +828,10 @@ public class WifiScanner { @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults() { validateChannel(); - Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0); + Bundle scanParams = new Bundle(); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + Message reply = + mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0, 0, scanParams); return reply.what == CMD_OP_SUCCEEDED; } @@ -856,6 +866,7 @@ public class WifiScanner { Bundle scanParams = new Bundle(); scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams); } @@ -870,7 +881,9 @@ public class WifiScanner { int key = removeListener(listener); if (key == INVALID_KEY) return; validateChannel(); - mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key); + Bundle scanParams = new Bundle(); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key, scanParams); } /** @@ -879,7 +892,10 @@ public class WifiScanner { */ public List<ScanResult> getSingleScanResults() { validateChannel(); - Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0); + Bundle scanParams = new Bundle(); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0, 0, + scanParams); if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) { return Arrays.asList(((ParcelableScanResults) reply.obj).getResults()); } diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java index 04bc55710dfd..aa526d248d14 100644 --- a/wifi/java/com/android/server/wifi/AbstractWifiService.java +++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java @@ -73,7 +73,7 @@ public abstract class AbstractWifiService extends IWifiManager.Stub { } @Override - public ParceledListSlice getConfiguredNetworks() { + public ParceledListSlice getConfiguredNetworks(String packageName) { throw new UnsupportedOperationException(); } @@ -188,17 +188,17 @@ public abstract class AbstractWifiService extends IWifiManager.Stub { } @Override - public void disconnect(String packageName) { + public boolean disconnect(String packageName) { throw new UnsupportedOperationException(); } @Override - public void reconnect(String packageName) { + public boolean reconnect(String packageName) { throw new UnsupportedOperationException(); } @Override - public void reassociate(String packageName) { + public boolean reassociate(String packageName) { throw new UnsupportedOperationException(); } |