diff options
61 files changed, 2107 insertions, 492 deletions
diff --git a/Android.bp b/Android.bp index 70b1fa0e793f..82589c1ecfe2 100644 --- a/Android.bp +++ b/Android.bp @@ -548,14 +548,6 @@ java_library { ":framework-statslog-gen", ], - logtags: [ - "core/java/android/app/admin/SecurityLogTags.logtags", - "core/java/android/content/EventLogTags.logtags", - "core/java/android/speech/tts/EventLogTags.logtags", - "core/java/android/net/EventLogTags.logtags", - "core/java/android/webkit/EventLogTags.logtags", - "core/java/com/android/internal/logging/EventLogTags.logtags", - ], aidl: { local_include_dirs: [ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 20213199f53b..8a4d29bb7bdb 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2557,8 +2557,8 @@ public final class ActivityThread extends ClientTransactionHandler { + " req=" + requestCode + " res=" + resultCode + " data=" + data); ArrayList<ResultInfo> list = new ArrayList<ResultInfo>(); list.add(new ResultInfo(id, requestCode, resultCode, data)); - final ClientTransaction clientTransaction = new ClientTransaction(mAppThread, token); - clientTransaction.addCallback(new ActivityResultItem(list)); + final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread, token); + clientTransaction.addCallback(ActivityResultItem.obtain(list)); try { mAppThread.scheduleTransaction(clientTransaction); } catch (RemoteException e) { diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java index a3fe686c4a08..a2b7d5809c4d 100644 --- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java @@ -25,17 +25,15 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Trace; +import java.util.Objects; + /** * Activity configuration changed callback. * @hide */ public class ActivityConfigurationChangeItem extends ClientTransactionItem { - private final Configuration mConfiguration; - - public ActivityConfigurationChangeItem(Configuration configuration) { - mConfiguration = configuration; - } + private Configuration mConfiguration; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -47,6 +45,29 @@ public class ActivityConfigurationChangeItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private ActivityConfigurationChangeItem() {} + + /** Obtain an instance initialized with provided params. */ + public static ActivityConfigurationChangeItem obtain(Configuration config) { + ActivityConfigurationChangeItem instance = + ObjectPool.obtain(ActivityConfigurationChangeItem.class); + if (instance == null) { + instance = new ActivityConfigurationChangeItem(); + } + instance.mConfiguration = config; + + return instance; + } + + @Override + public void recycle() { + mConfiguration = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -80,7 +101,7 @@ public class ActivityConfigurationChangeItem extends ClientTransactionItem { return false; } final ActivityConfigurationChangeItem other = (ActivityConfigurationChangeItem) o; - return mConfiguration.equals(other.mConfiguration); + return Objects.equals(mConfiguration, other.mConfiguration); } @Override diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java index 3a3d5b981fb4..73b5ec440441 100644 --- a/core/java/android/app/servertransaction/ActivityResultItem.java +++ b/core/java/android/app/servertransaction/ActivityResultItem.java @@ -27,6 +27,7 @@ import android.os.Parcelable; import android.os.Trace; import java.util.List; +import java.util.Objects; /** * Activity result delivery callback. @@ -34,11 +35,7 @@ import java.util.List; */ public class ActivityResultItem extends ClientTransactionItem { - private final List<ResultInfo> mResultInfoList; - - public ActivityResultItem(List<ResultInfo> resultInfos) { - mResultInfoList = resultInfos; - } + private List<ResultInfo> mResultInfoList; @Override public int getPreExecutionState() { @@ -54,6 +51,28 @@ public class ActivityResultItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private ActivityResultItem() {} + + /** Obtain an instance initialized with provided params. */ + public static ActivityResultItem obtain(List<ResultInfo> resultInfoList) { + ActivityResultItem instance = ObjectPool.obtain(ActivityResultItem.class); + if (instance == null) { + instance = new ActivityResultItem(); + } + instance.mResultInfoList = resultInfoList; + + return instance; + } + + @Override + public void recycle() { + mResultInfoList = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -87,7 +106,7 @@ public class ActivityResultItem extends ClientTransactionItem { return false; } final ActivityResultItem other = (ActivityResultItem) o; - return mResultInfoList.equals(other.mResultInfoList); + return Objects.equals(mResultInfoList, other.mResultInfoList); } @Override diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java index e3473bfd0ae8..c91e0ca5ffc1 100644 --- a/core/java/android/app/servertransaction/BaseClientRequest.java +++ b/core/java/android/app/servertransaction/BaseClientRequest.java @@ -24,7 +24,7 @@ import android.os.IBinder; * Each of them can be prepared before scheduling and, eventually, executed. * @hide */ -public interface BaseClientRequest { +public interface BaseClientRequest extends ObjectPoolItem { /** * Prepare the client request before scheduling. diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 7a5896261aac..764ceede5d20 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -37,7 +37,7 @@ import java.util.Objects; * @see ActivityLifecycleItem * @hide */ -public class ClientTransaction implements Parcelable { +public class ClientTransaction implements Parcelable, ObjectPoolItem { /** A list of individual callbacks to a client. */ private List<ClientTransactionItem> mActivityCallbacks; @@ -54,11 +54,6 @@ public class ClientTransaction implements Parcelable { /** Target client activity. Might be null if the entire transaction is targeting an app. */ private IBinder mActivityToken; - public ClientTransaction(IApplicationThread client, IBinder activityToken) { - mClient = client; - mActivityToken = activityToken; - } - /** * Add a message to the end of the sequence of callbacks. * @param activityCallback A single message that can contain a lifecycle request/callback. @@ -127,6 +122,41 @@ public class ClientTransaction implements Parcelable { } + // ObjectPoolItem implementation + + private ClientTransaction() {} + + /** Obtain an instance initialized with provided params. */ + public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) { + ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class); + if (instance == null) { + instance = new ClientTransaction(); + } + instance.mClient = client; + instance.mActivityToken = activityToken; + + return instance; + } + + @Override + public void recycle() { + if (mActivityCallbacks != null) { + int size = mActivityCallbacks.size(); + for (int i = 0; i < size; i++) { + mActivityCallbacks.get(i).recycle(); + } + mActivityCallbacks.clear(); + } + if (mLifecycleStateRequest != null) { + mLifecycleStateRequest.recycle(); + mLifecycleStateRequest = null; + } + mClient = null; + mActivityToken = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java index ee1effa6c2ab..4ab7251e4d8a 100644 --- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java @@ -21,17 +21,15 @@ import android.content.res.Configuration; import android.os.IBinder; import android.os.Parcel; +import java.util.Objects; + /** * App configuration change message. * @hide */ public class ConfigurationChangeItem extends ClientTransactionItem { - private final Configuration mConfiguration; - - public ConfigurationChangeItem(Configuration configuration) { - mConfiguration = new Configuration(configuration); - } + private Configuration mConfiguration; @Override public void preExecute(android.app.ClientTransactionHandler client, IBinder token) { @@ -44,6 +42,29 @@ public class ConfigurationChangeItem extends ClientTransactionItem { client.handleConfigurationChanged(mConfiguration); } + + // ObjectPoolItem implementation + + private ConfigurationChangeItem() {} + + /** Obtain an instance initialized with provided params. */ + public static ConfigurationChangeItem obtain(Configuration config) { + ConfigurationChangeItem instance = ObjectPool.obtain(ConfigurationChangeItem.class); + if (instance == null) { + instance = new ConfigurationChangeItem(); + } + instance.mConfiguration = config; + + return instance; + } + + @Override + public void recycle() { + mConfiguration = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -77,7 +98,7 @@ public class ConfigurationChangeItem extends ClientTransactionItem { return false; } final ConfigurationChangeItem other = (ConfigurationChangeItem) o; - return mConfiguration.equals(other.mConfiguration); + return Objects.equals(mConfiguration, other.mConfiguration); } @Override diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java index 3012a7a474e6..83da5f33c62a 100644 --- a/core/java/android/app/servertransaction/DestroyActivityItem.java +++ b/core/java/android/app/servertransaction/DestroyActivityItem.java @@ -29,13 +29,8 @@ import android.os.Trace; */ public class DestroyActivityItem extends ActivityLifecycleItem { - private final boolean mFinished; - private final int mConfigChanges; - - public DestroyActivityItem(boolean finished, int configChanges) { - mFinished = finished; - mConfigChanges = configChanges; - } + private boolean mFinished; + private int mConfigChanges; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -52,6 +47,30 @@ public class DestroyActivityItem extends ActivityLifecycleItem { } + // ObjectPoolItem implementation + + private DestroyActivityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static DestroyActivityItem obtain(boolean finished, int configChanges) { + DestroyActivityItem instance = ObjectPool.obtain(DestroyActivityItem.class); + if (instance == null) { + instance = new DestroyActivityItem(); + } + instance.mFinished = finished; + instance.mConfigChanges = configChanges; + + return instance; + } + + @Override + public void recycle() { + mFinished = false; + mConfigChanges = 0; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index e39042f43d51..7be82bf9f505 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -45,43 +45,21 @@ import java.util.Objects; */ public class LaunchActivityItem extends ClientTransactionItem { - private final Intent mIntent; - private final int mIdent; - private final ActivityInfo mInfo; - private final Configuration mCurConfig; - private final Configuration mOverrideConfig; - private final CompatibilityInfo mCompatInfo; - private final String mReferrer; - private final IVoiceInteractor mVoiceInteractor; - private final int mProcState; - private final Bundle mState; - private final PersistableBundle mPersistentState; - private final List<ResultInfo> mPendingResults; - private final List<ReferrerIntent> mPendingNewIntents; - private final boolean mIsForward; - private final ProfilerInfo mProfilerInfo; - - public LaunchActivityItem(Intent intent, int ident, ActivityInfo info, - Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, - String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, - PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<ReferrerIntent> pendingNewIntents, boolean isForward, ProfilerInfo profilerInfo) { - mIntent = intent; - mIdent = ident; - mInfo = info; - mCurConfig = curConfig; - mOverrideConfig = overrideConfig; - mCompatInfo = compatInfo; - mReferrer = referrer; - mVoiceInteractor = voiceInteractor; - mProcState = procState; - mState = state; - mPersistentState = persistentState; - mPendingResults = pendingResults; - mPendingNewIntents = pendingNewIntents; - mIsForward = isForward; - mProfilerInfo = profilerInfo; - } + private Intent mIntent; + private int mIdent; + private ActivityInfo mInfo; + private Configuration mCurConfig; + private Configuration mOverrideConfig; + private CompatibilityInfo mCompatInfo; + private String mReferrer; + private IVoiceInteractor mVoiceInteractor; + private int mProcState; + private Bundle mState; + private PersistableBundle mPersistentState; + private List<ResultInfo> mPendingResults; + private List<ReferrerIntent> mPendingNewIntents; + private boolean mIsForward; + private ProfilerInfo mProfilerInfo; @Override public void preExecute(ClientTransactionHandler client, IBinder token) { @@ -102,6 +80,35 @@ public class LaunchActivityItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private LaunchActivityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static LaunchActivityItem obtain(Intent intent, int ident, ActivityInfo info, + Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, + String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, + PersistableBundle persistentState, List<ResultInfo> pendingResults, + List<ReferrerIntent> pendingNewIntents, boolean isForward, ProfilerInfo profilerInfo) { + LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class); + if (instance == null) { + instance = new LaunchActivityItem(); + } + setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer, + voiceInteractor, procState, state, persistentState, pendingResults, + pendingNewIntents, isForward, profilerInfo); + + return instance; + } + + @Override + public void recycle() { + setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null, + false, null); + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write from Parcel. */ @@ -114,7 +121,7 @@ public class LaunchActivityItem extends ClientTransactionItem { dest.writeTypedObject(mOverrideConfig, flags); dest.writeTypedObject(mCompatInfo, flags); dest.writeString(mReferrer); - dest.writeStrongBinder(mVoiceInteractor != null ? mVoiceInteractor.asBinder() : null); + dest.writeStrongInterface(mVoiceInteractor); dest.writeInt(mProcState); dest.writeBundle(mState); dest.writePersistableBundle(mPersistentState); @@ -126,21 +133,16 @@ public class LaunchActivityItem extends ClientTransactionItem { /** Read from Parcel. */ private LaunchActivityItem(Parcel in) { - mIntent = in.readTypedObject(Intent.CREATOR); - mIdent = in.readInt(); - mInfo = in.readTypedObject(ActivityInfo.CREATOR); - mCurConfig = in.readTypedObject(Configuration.CREATOR); - mOverrideConfig = in.readTypedObject(Configuration.CREATOR); - mCompatInfo = in.readTypedObject(CompatibilityInfo.CREATOR); - mReferrer = in.readString(); - mVoiceInteractor = (IVoiceInteractor) in.readStrongBinder(); - mProcState = in.readInt(); - mState = in.readBundle(getClass().getClassLoader()); - mPersistentState = in.readPersistableBundle(getClass().getClassLoader()); - mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR); - mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR); - mIsForward = in.readBoolean(); - mProfilerInfo = in.readTypedObject(ProfilerInfo.CREATOR); + setValues(this, in.readTypedObject(Intent.CREATOR), in.readInt(), + in.readTypedObject(ActivityInfo.CREATOR), in.readTypedObject(Configuration.CREATOR), + in.readTypedObject(Configuration.CREATOR), + in.readTypedObject(CompatibilityInfo.CREATOR), in.readString(), + IVoiceInteractor.Stub.asInterface(in.readStrongBinder()), in.readInt(), + in.readBundle(getClass().getClassLoader()), + in.readPersistableBundle(getClass().getClassLoader()), + in.createTypedArrayList(ResultInfo.CREATOR), + in.createTypedArrayList(ReferrerIntent.CREATOR), in.readBoolean(), + in.readTypedObject(ProfilerInfo.CREATOR)); } public static final Creator<LaunchActivityItem> CREATOR = @@ -163,7 +165,9 @@ public class LaunchActivityItem extends ClientTransactionItem { return false; } final LaunchActivityItem other = (LaunchActivityItem) o; - return mIntent.filterEquals(other.mIntent) && mIdent == other.mIdent + final boolean intentsEqual = (mIntent == null && other.mIntent == null) + || (mIntent != null && mIntent.filterEquals(other.mIntent)); + return intentsEqual && mIdent == other.mIdent && activityInfoEqual(other.mInfo) && Objects.equals(mCurConfig, other.mCurConfig) && Objects.equals(mOverrideConfig, other.mOverrideConfig) && Objects.equals(mCompatInfo, other.mCompatInfo) @@ -196,7 +200,11 @@ public class LaunchActivityItem extends ClientTransactionItem { } private boolean activityInfoEqual(ActivityInfo other) { - return mInfo.flags == other.flags && mInfo.maxAspectRatio == other.maxAspectRatio + if (mInfo == null) { + return other == null; + } + return other != null && mInfo.flags == other.flags + && mInfo.maxAspectRatio == other.maxAspectRatio && Objects.equals(mInfo.launchToken, other.launchToken) && Objects.equals(mInfo.getComponentName(), other.getComponentName()); } @@ -231,4 +239,28 @@ public class LaunchActivityItem extends ClientTransactionItem { + ",pendingNewIntents=" + mPendingNewIntents + ",profilerInfo=" + mProfilerInfo + "}"; } + + // Using the same method to set and clear values to make sure we don't forget anything + private static void setValues(LaunchActivityItem instance, Intent intent, int ident, + ActivityInfo info, Configuration curConfig, Configuration overrideConfig, + CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, + int procState, Bundle state, PersistableBundle persistentState, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, + boolean isForward, ProfilerInfo profilerInfo) { + instance.mIntent = intent; + instance.mIdent = ident; + instance.mInfo = info; + instance.mCurConfig = curConfig; + instance.mOverrideConfig = overrideConfig; + instance.mCompatInfo = compatInfo; + instance.mReferrer = referrer; + instance.mVoiceInteractor = voiceInteractor; + instance.mProcState = procState; + instance.mState = state; + instance.mPersistentState = persistentState; + instance.mPendingResults = pendingResults; + instance.mPendingNewIntents = pendingNewIntents; + instance.mIsForward = isForward; + instance.mProfilerInfo = profilerInfo; + } } diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java index ee87261217fa..b3dddfb37eaa 100644 --- a/core/java/android/app/servertransaction/MoveToDisplayItem.java +++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java @@ -24,19 +24,16 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Trace; +import java.util.Objects; + /** * Activity move to a different display message. * @hide */ public class MoveToDisplayItem extends ClientTransactionItem { - private final int mTargetDisplayId; - private final Configuration mConfiguration; - - public MoveToDisplayItem(int targetDisplayId, Configuration configuration) { - mTargetDisplayId = targetDisplayId; - mConfiguration = configuration; - } + private int mTargetDisplayId; + private Configuration mConfiguration; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -47,6 +44,30 @@ public class MoveToDisplayItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private MoveToDisplayItem() {} + + /** Obtain an instance initialized with provided params. */ + public static MoveToDisplayItem obtain(int targetDisplayId, Configuration configuration) { + MoveToDisplayItem instance = ObjectPool.obtain(MoveToDisplayItem.class); + if (instance == null) { + instance = new MoveToDisplayItem(); + } + instance.mTargetDisplayId = targetDisplayId; + instance.mConfiguration = configuration; + + return instance; + } + + @Override + public void recycle() { + mTargetDisplayId = 0; + mConfiguration = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -82,7 +103,7 @@ public class MoveToDisplayItem extends ClientTransactionItem { } final MoveToDisplayItem other = (MoveToDisplayItem) o; return mTargetDisplayId == other.mTargetDisplayId - && mConfiguration.equals(other.mConfiguration); + && Objects.equals(mConfiguration, other.mConfiguration); } @Override diff --git a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java index 04ddb5e1378c..c3022d6facc7 100644 --- a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java +++ b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java @@ -21,6 +21,8 @@ import android.content.res.Configuration; import android.os.IBinder; import android.os.Parcel; +import java.util.Objects; + /** * Multi-window mode change message. * @hide @@ -29,14 +31,8 @@ import android.os.Parcel; // communicate multi-window mode change with WindowConfiguration. public class MultiWindowModeChangeItem extends ClientTransactionItem { - private final boolean mIsInMultiWindowMode; - private final Configuration mOverrideConfig; - - public MultiWindowModeChangeItem(boolean isInMultiWindowMode, - Configuration overrideConfig) { - mIsInMultiWindowMode = isInMultiWindowMode; - mOverrideConfig = overrideConfig; - } + private boolean mIsInMultiWindowMode; + private Configuration mOverrideConfig; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -45,6 +41,31 @@ public class MultiWindowModeChangeItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private MultiWindowModeChangeItem() {} + + /** Obtain an instance initialized with provided params. */ + public static MultiWindowModeChangeItem obtain(boolean isInMultiWindowMode, + Configuration overrideConfig) { + MultiWindowModeChangeItem instance = ObjectPool.obtain(MultiWindowModeChangeItem.class); + if (instance == null) { + instance = new MultiWindowModeChangeItem(); + } + instance.mIsInMultiWindowMode = isInMultiWindowMode; + instance.mOverrideConfig = overrideConfig; + + return instance; + } + + @Override + public void recycle() { + mIsInMultiWindowMode = false; + mOverrideConfig = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -81,7 +102,7 @@ public class MultiWindowModeChangeItem extends ClientTransactionItem { } final MultiWindowModeChangeItem other = (MultiWindowModeChangeItem) o; return mIsInMultiWindowMode == other.mIsInMultiWindowMode - && mOverrideConfig.equals(other.mOverrideConfig); + && Objects.equals(mOverrideConfig, other.mOverrideConfig); } @Override diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java index d01b455e5390..7dfde73c0534 100644 --- a/core/java/android/app/servertransaction/NewIntentItem.java +++ b/core/java/android/app/servertransaction/NewIntentItem.java @@ -25,6 +25,7 @@ import android.os.Trace; import com.android.internal.content.ReferrerIntent; import java.util.List; +import java.util.Objects; /** * New intent message. @@ -32,13 +33,8 @@ import java.util.List; */ public class NewIntentItem extends ClientTransactionItem { - private final List<ReferrerIntent> mIntents; - private final boolean mPause; - - public NewIntentItem(List<ReferrerIntent> intents, boolean pause) { - mIntents = intents; - mPause = pause; - } + private List<ReferrerIntent> mIntents; + private boolean mPause; // TODO(lifecycler): Switch new intent handling to this scheme. /*@Override @@ -60,6 +56,30 @@ public class NewIntentItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private NewIntentItem() {} + + /** Obtain an instance initialized with provided params. */ + public static NewIntentItem obtain(List<ReferrerIntent> intents, boolean pause) { + NewIntentItem instance = ObjectPool.obtain(NewIntentItem.class); + if (instance == null) { + instance = new NewIntentItem(); + } + instance.mIntents = intents; + instance.mPause = pause; + + return instance; + } + + @Override + public void recycle() { + mIntents = null; + mPause = false; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -95,7 +115,7 @@ public class NewIntentItem extends ClientTransactionItem { return false; } final NewIntentItem other = (NewIntentItem) o; - return mPause == other.mPause && mIntents.equals(other.mIntents); + return mPause == other.mPause && Objects.equals(mIntents, other.mIntents); } @Override diff --git a/core/java/android/app/servertransaction/ObjectPool.java b/core/java/android/app/servertransaction/ObjectPool.java new file mode 100644 index 000000000000..98121253f486 --- /dev/null +++ b/core/java/android/app/servertransaction/ObjectPool.java @@ -0,0 +1,73 @@ +/* + * Copyright 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.app.servertransaction; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +/** + * An object pool that can provide reused objects if available. + * @hide + */ +class ObjectPool { + + private static final Object sPoolSync = new Object(); + private static final Map<Class, LinkedList<? extends ObjectPoolItem>> sPoolMap = + new HashMap<>(); + + private static final int MAX_POOL_SIZE = 50; + + /** + * Obtain an instance of a specific class from the pool + * @param itemClass The class of the object we're looking for. + * @return An instance or null if there is none. + */ + public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) { + synchronized (sPoolSync) { + @SuppressWarnings("unchecked") + LinkedList<T> itemPool = (LinkedList<T>) sPoolMap.get(itemClass); + if (itemPool != null && !itemPool.isEmpty()) { + return itemPool.poll(); + } + return null; + } + } + + /** + * Recycle the object to the pool. The object should be properly cleared before this. + * @param item The object to recycle. + * @see ObjectPoolItem#recycle() + */ + public static <T extends ObjectPoolItem> void recycle(T item) { + synchronized (sPoolSync) { + @SuppressWarnings("unchecked") + LinkedList<T> itemPool = (LinkedList<T>) sPoolMap.get(item.getClass()); + if (itemPool == null) { + itemPool = new LinkedList<>(); + sPoolMap.put(item.getClass(), itemPool); + } + if (itemPool.contains(item)) { + throw new IllegalStateException("Trying to recycle already recycled item"); + } + + if (itemPool.size() < MAX_POOL_SIZE) { + itemPool.add(item); + } + } + } +} diff --git a/core/java/android/app/servertransaction/ObjectPoolItem.java b/core/java/android/app/servertransaction/ObjectPoolItem.java new file mode 100644 index 000000000000..17bd4f30640f --- /dev/null +++ b/core/java/android/app/servertransaction/ObjectPoolItem.java @@ -0,0 +1,29 @@ +/* + * Copyright 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.app.servertransaction; + +/** + * Base interface for all lifecycle items that can be put in object pool. + * @hide + */ +public interface ObjectPoolItem { + /** + * Clear the contents of the item and putting it to a pool. The implementation should call + * {@link ObjectPool#recycle(ObjectPoolItem)} passing itself. + */ + void recycle(); +} diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java index 634da04a09bf..880fef73c6f2 100644 --- a/core/java/android/app/servertransaction/PauseActivityItem.java +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -33,23 +33,10 @@ public class PauseActivityItem extends ActivityLifecycleItem { private static final String TAG = "PauseActivityItem"; - private final boolean mFinished; - private final boolean mUserLeaving; - private final int mConfigChanges; - private final boolean mDontReport; - - public PauseActivityItem() { - this(false /* finished */, false /* userLeaving */, 0 /* configChanges */, - true /* dontReport */); - } - - public PauseActivityItem(boolean finished, boolean userLeaving, int configChanges, - boolean dontReport) { - mFinished = finished; - mUserLeaving = userLeaving; - mConfigChanges = configChanges; - mDontReport = dontReport; - } + private boolean mFinished; + private boolean mUserLeaving; + private int mConfigChanges; + private boolean mDontReport; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -79,6 +66,49 @@ public class PauseActivityItem extends ActivityLifecycleItem { } } + + // ObjectPoolItem implementation + + private PauseActivityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static PauseActivityItem obtain(boolean finished, boolean userLeaving, int configChanges, + boolean dontReport) { + PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class); + if (instance == null) { + instance = new PauseActivityItem(); + } + instance.mFinished = finished; + instance.mUserLeaving = userLeaving; + instance.mConfigChanges = configChanges; + instance.mDontReport = dontReport; + + return instance; + } + + /** Obtain an instance initialized with default params. */ + public static PauseActivityItem obtain() { + PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class); + if (instance == null) { + instance = new PauseActivityItem(); + } + instance.mFinished = false; + instance.mUserLeaving = false; + instance.mConfigChanges = 0; + instance.mDontReport = true; + + return instance; + } + + @Override + public void recycle() { + mFinished = false; + mUserLeaving = false; + mConfigChanges = 0; + mDontReport = false; + ObjectPool.recycle(this); + } + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/servertransaction/PipModeChangeItem.java b/core/java/android/app/servertransaction/PipModeChangeItem.java index 7c74e6f76656..b999cd7e295e 100644 --- a/core/java/android/app/servertransaction/PipModeChangeItem.java +++ b/core/java/android/app/servertransaction/PipModeChangeItem.java @@ -21,6 +21,8 @@ import android.content.res.Configuration; import android.os.IBinder; import android.os.Parcel; +import java.util.Objects; + /** * Picture in picture mode change message. * @hide @@ -29,13 +31,8 @@ import android.os.Parcel; // communicate multi-window mode change with WindowConfiguration. public class PipModeChangeItem extends ClientTransactionItem { - private final boolean mIsInPipMode; - private final Configuration mOverrideConfig; - - public PipModeChangeItem(boolean isInPipMode, Configuration overrideConfig) { - mIsInPipMode = isInPipMode; - mOverrideConfig = overrideConfig; - } + private boolean mIsInPipMode; + private Configuration mOverrideConfig; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -44,6 +41,30 @@ public class PipModeChangeItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private PipModeChangeItem() {} + + /** Obtain an instance initialized with provided params. */ + public static PipModeChangeItem obtain(boolean isInPipMode, Configuration overrideConfig) { + PipModeChangeItem instance = ObjectPool.obtain(PipModeChangeItem.class); + if (instance == null) { + instance = new PipModeChangeItem(); + } + instance.mIsInPipMode = isInPipMode; + instance.mOverrideConfig = overrideConfig; + + return instance; + } + + @Override + public void recycle() { + mIsInPipMode = false; + mOverrideConfig = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -78,7 +99,8 @@ public class PipModeChangeItem extends ClientTransactionItem { return false; } final PipModeChangeItem other = (PipModeChangeItem) o; - return mIsInPipMode == other.mIsInPipMode && mOverrideConfig.equals(other.mOverrideConfig); + return mIsInPipMode == other.mIsInPipMode + && Objects.equals(mOverrideConfig, other.mOverrideConfig); } @Override diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java index d659b80bb54f..9249c6e8ed54 100644 --- a/core/java/android/app/servertransaction/ResumeActivityItem.java +++ b/core/java/android/app/servertransaction/ResumeActivityItem.java @@ -33,21 +33,9 @@ public class ResumeActivityItem extends ActivityLifecycleItem { private static final String TAG = "ResumeActivityItem"; - private final int mProcState; - private final boolean mUpdateProcState; - private final boolean mIsForward; - - public ResumeActivityItem(boolean isForward) { - mProcState = ActivityManager.PROCESS_STATE_UNKNOWN; - mUpdateProcState = false; - mIsForward = isForward; - } - - public ResumeActivityItem(int procState, boolean isForward) { - mProcState = procState; - mUpdateProcState = true; - mIsForward = isForward; - } + private int mProcState; + private boolean mUpdateProcState; + private boolean mIsForward; @Override public void preExecute(ClientTransactionHandler client, IBinder token) { @@ -81,6 +69,45 @@ public class ResumeActivityItem extends ActivityLifecycleItem { } + // ObjectPoolItem implementation + + private ResumeActivityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static ResumeActivityItem obtain(int procState, boolean isForward) { + ResumeActivityItem instance = ObjectPool.obtain(ResumeActivityItem.class); + if (instance == null) { + instance = new ResumeActivityItem(); + } + instance.mProcState = procState; + instance.mUpdateProcState = true; + instance.mIsForward = isForward; + + return instance; + } + + /** Obtain an instance initialized with provided params. */ + public static ResumeActivityItem obtain(boolean isForward) { + ResumeActivityItem instance = ObjectPool.obtain(ResumeActivityItem.class); + if (instance == null) { + instance = new ResumeActivityItem(); + } + instance.mProcState = ActivityManager.PROCESS_STATE_UNKNOWN; + instance.mUpdateProcState = false; + instance.mIsForward = isForward; + + return instance; + } + + @Override + public void recycle() { + mProcState = ActivityManager.PROCESS_STATE_UNKNOWN; + mUpdateProcState = false; + mIsForward = false; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java index 6e49386b9c38..5c5c3041344f 100644 --- a/core/java/android/app/servertransaction/StopActivityItem.java +++ b/core/java/android/app/servertransaction/StopActivityItem.java @@ -31,13 +31,8 @@ public class StopActivityItem extends ActivityLifecycleItem { private static final String TAG = "StopActivityItem"; - private final boolean mShowWindow; - private final int mConfigChanges; - - public StopActivityItem(boolean showWindow, int configChanges) { - mShowWindow = showWindow; - mConfigChanges = configChanges; - } + private boolean mShowWindow; + private int mConfigChanges; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -59,6 +54,30 @@ public class StopActivityItem extends ActivityLifecycleItem { } + // ObjectPoolItem implementation + + private StopActivityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static StopActivityItem obtain(boolean showWindow, int configChanges) { + StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class); + if (instance == null) { + instance = new StopActivityItem(); + } + instance.mShowWindow = showWindow; + instance.mConfigChanges = configChanges; + + return instance; + } + + @Override + public void recycle() { + mShowWindow = false; + mConfigChanges = 0; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index bd24824f142d..5b0ea6b1f9d4 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -243,8 +243,6 @@ public class TransactionExecutor { } private static void log(String message) { - if (DEBUG_RESOLVER) { - Slog.d(TAG, message); - } + if (DEBUG_RESOLVER) Slog.d(TAG, message); } } diff --git a/core/java/android/app/servertransaction/WindowVisibilityItem.java b/core/java/android/app/servertransaction/WindowVisibilityItem.java index 6fcdcfa9efd6..d9956b1348b1 100644 --- a/core/java/android/app/servertransaction/WindowVisibilityItem.java +++ b/core/java/android/app/servertransaction/WindowVisibilityItem.java @@ -29,11 +29,7 @@ import android.os.Trace; */ public class WindowVisibilityItem extends ClientTransactionItem { - private final boolean mShowWindow; - - public WindowVisibilityItem(boolean showWindow) { - mShowWindow = showWindow; - } + private boolean mShowWindow; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -44,6 +40,28 @@ public class WindowVisibilityItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private WindowVisibilityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static WindowVisibilityItem obtain(boolean showWindow) { + WindowVisibilityItem instance = ObjectPool.obtain(WindowVisibilityItem.class); + if (instance == null) { + instance = new WindowVisibilityItem(); + } + instance.mShowWindow = showWindow; + + return instance; + } + + @Override + public void recycle() { + mShowWindow = false; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1d4477a1c4c1..6b1632a0a693 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10164,6 +10164,16 @@ public final class Settings { public static final String POLICY_CONTROL = "policy_control"; /** + * {@link android.view.DisplayCutout DisplayCutout} emulation mode. + * + * @hide + */ + public static final String EMULATE_DISPLAY_CUTOUT = "emulate_display_cutout"; + + /** @hide */ public static final int EMULATE_DISPLAY_CUTOUT_OFF = 0; + /** @hide */ public static final int EMULATE_DISPLAY_CUTOUT_ON = 1; + + /** * Defines global zen mode. ZEN_MODE_OFF, ZEN_MODE_IMPORTANT_INTERRUPTIONS, * or ZEN_MODE_NO_INTERRUPTIONS. * diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 905c0715ecb8..7c2c12f56e5f 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1268,6 +1268,35 @@ public interface WindowManager extends ViewManager { }, formatToHexString = true) public int flags; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + flag = true, + value = { + LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA, + }) + @interface Flags2 {} + + /** + * Window flag: allow placing the window within the area that overlaps with the + * display cutout. + * + * <p> + * The window must correctly position its contents to take the display cutout into account. + * + * @see DisplayCutout + * @hide for now + */ + public static final long FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA = 0x00000001; + + /** + * Various behavioral options/flags. Default is none. + * + * @see #FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA + * @hide for now + */ + @Flags2 public long flags2; + /** * If the window has requested hardware acceleration, but this is not * allowed in the process it is in, then still render it as if it is diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java index ea2dd59a4313..b1f855246320 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java @@ -42,7 +42,7 @@ public class ClientTransactionTests { ClientTransactionHandler clientTransactionHandler = mock(ClientTransactionHandler.class); IBinder token = mock(IBinder.class); - ClientTransaction transaction = new ClientTransaction(null /* client */, + ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token /* activityToken */); transaction.addCallback(callback1); transaction.addCallback(callback2); diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java new file mode 100644 index 000000000000..aefc47e95512 --- /dev/null +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -0,0 +1,287 @@ +/* + * Copyright 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.app.servertransaction; + +import static android.app.servertransaction.TestUtils.config; +import static android.app.servertransaction.TestUtils.referrerIntentList; +import static android.app.servertransaction.TestUtils.resultInfoList; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.os.Binder; +import android.os.Bundle; +import android.os.PersistableBundle; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class ObjectPoolTests { + + // 1. Check if two obtained objects from pool are not the same. + // 2. Check if the state of the object is cleared after recycling. + // 3. Check if the same object is obtained from pool after recycling. + + @Test + public void testRecycleActivityConfigurationChangeItem() { + ActivityConfigurationChangeItem emptyItem = ActivityConfigurationChangeItem.obtain(null); + ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(config()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + ActivityConfigurationChangeItem item2 = ActivityConfigurationChangeItem.obtain(config()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleActivityResultItem() { + ActivityResultItem emptyItem = ActivityResultItem.obtain(null); + ActivityResultItem item = ActivityResultItem.obtain(resultInfoList()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + ActivityResultItem item2 = ActivityResultItem.obtain(resultInfoList()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleConfigurationChangeItem() { + ConfigurationChangeItem emptyItem = ConfigurationChangeItem.obtain(null); + ConfigurationChangeItem item = ConfigurationChangeItem.obtain(config()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + ConfigurationChangeItem item2 = ConfigurationChangeItem.obtain(config()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleDestroyActivityItem() { + DestroyActivityItem emptyItem = DestroyActivityItem.obtain(false, 0); + DestroyActivityItem item = DestroyActivityItem.obtain(true, 117); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + DestroyActivityItem item2 = DestroyActivityItem.obtain(true, 14); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleLaunchActivityItem() { + Intent intent = new Intent("action"); + int ident = 57; + ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.flags = 42; + activityInfo.maxAspectRatio = 2.4f; + activityInfo.launchToken = "token"; + activityInfo.applicationInfo = new ApplicationInfo(); + activityInfo.packageName = "packageName"; + activityInfo.name = "name"; + Configuration overrideConfig = new Configuration(); + overrideConfig.assetsSeq = 5; + CompatibilityInfo compat = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; + String referrer = "referrer"; + int procState = 4; + Bundle bundle = new Bundle(); + bundle.putString("key", "value"); + PersistableBundle persistableBundle = new PersistableBundle(); + persistableBundle.putInt("k", 4); + + LaunchActivityItem emptyItem = LaunchActivityItem.obtain(null, 0, null, null, null, null, + null, null, 0, null, null, null, null, false, null); + LaunchActivityItem item = LaunchActivityItem.obtain(intent, ident, activityInfo, + config(), overrideConfig, compat, referrer, null /* voiceInteractor */, + procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(), + true /* isForward */, null /* profilerInfo */); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + LaunchActivityItem item2 = LaunchActivityItem.obtain(intent, ident, activityInfo, + config(), overrideConfig, compat, referrer, null /* voiceInteractor */, + procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(), + true /* isForward */, null /* profilerInfo */); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleMoveToDisplayItem() { + MoveToDisplayItem emptyItem = MoveToDisplayItem.obtain(0, null); + MoveToDisplayItem item = MoveToDisplayItem.obtain(4, config()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + MoveToDisplayItem item2 = MoveToDisplayItem.obtain(3, config()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleMultiWindowModeChangeItem() { + MultiWindowModeChangeItem emptyItem = MultiWindowModeChangeItem.obtain(false, null); + MultiWindowModeChangeItem item = MultiWindowModeChangeItem.obtain(true, config()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + MultiWindowModeChangeItem item2 = MultiWindowModeChangeItem.obtain(true, config()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleNewIntentItem() { + NewIntentItem emptyItem = NewIntentItem.obtain(null, false); + NewIntentItem item = NewIntentItem.obtain(referrerIntentList(), true); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + NewIntentItem item2 = NewIntentItem.obtain(referrerIntentList(), true); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecyclePauseActivityItemItem() { + PauseActivityItem emptyItem = PauseActivityItem.obtain(false, false, 0, false); + PauseActivityItem item = PauseActivityItem.obtain(true, true, 5, true); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + PauseActivityItem item2 = PauseActivityItem.obtain(true, false, 5, true); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecyclePipModeChangeItem() { + PipModeChangeItem emptyItem = PipModeChangeItem.obtain(false, null); + PipModeChangeItem item = PipModeChangeItem.obtain(true, config()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + PipModeChangeItem item2 = PipModeChangeItem.obtain(true, config()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleResumeActivityItem() { + ResumeActivityItem emptyItem = ResumeActivityItem.obtain(false); + ResumeActivityItem item = ResumeActivityItem.obtain(3, true); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + ResumeActivityItem item2 = ResumeActivityItem.obtain(2, true); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleStopItem() { + StopActivityItem emptyItem = StopActivityItem.obtain(false, 0); + StopActivityItem item = StopActivityItem.obtain(true, 4); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + StopActivityItem item2 = StopActivityItem.obtain(true, 3); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleWindowVisibleItem() { + WindowVisibilityItem emptyItem = WindowVisibilityItem.obtain(false); + WindowVisibilityItem item = WindowVisibilityItem.obtain(true); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + WindowVisibilityItem item2 = WindowVisibilityItem.obtain(true); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleClientTransaction() { + ClientTransaction emptyItem = ClientTransaction.obtain(null, null); + ClientTransaction item = ClientTransaction.obtain(null, new Binder()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + ClientTransaction item2 = ClientTransaction.obtain(null, new Binder()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } +} diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java new file mode 100644 index 000000000000..e92351609256 --- /dev/null +++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java @@ -0,0 +1,74 @@ +/* + * Copyright 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.app.servertransaction; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; + +import android.app.ResultInfo; +import android.content.Intent; +import android.content.res.Configuration; + +import com.android.internal.content.ReferrerIntent; + +import java.util.ArrayList; +import java.util.List; + +class TestUtils { + + static Configuration config() { + Configuration config = new Configuration(); + config.densityDpi = 10; + config.fontScale = 0.3f; + config.screenHeightDp = 15; + config.orientation = ORIENTATION_LANDSCAPE; + return config; + } + + static List<ResultInfo> resultInfoList() { + String resultWho1 = "resultWho1"; + int requestCode1 = 7; + int resultCode1 = 4; + Intent data1 = new Intent("action1"); + ResultInfo resultInfo1 = new ResultInfo(resultWho1, requestCode1, resultCode1, data1); + + String resultWho2 = "resultWho2"; + int requestCode2 = 8; + int resultCode2 = 6; + Intent data2 = new Intent("action2"); + ResultInfo resultInfo2 = new ResultInfo(resultWho2, requestCode2, resultCode2, data2); + + List<ResultInfo> resultInfoList = new ArrayList<>(); + resultInfoList.add(resultInfo1); + resultInfoList.add(resultInfo2); + + return resultInfoList; + } + + static List<ReferrerIntent> referrerIntentList() { + Intent intent1 = new Intent("action1"); + ReferrerIntent referrerIntent1 = new ReferrerIntent(intent1, "referrer1"); + + Intent intent2 = new Intent("action2"); + ReferrerIntent referrerIntent2 = new ReferrerIntent(intent2, "referrer2"); + + List<ReferrerIntent> referrerIntents = new ArrayList<>(); + referrerIntents.add(referrerIntent1); + referrerIntents.add(referrerIntent2); + + return referrerIntents; + } +} diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java index d2830047505b..e575650393f0 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.when; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.IBinder; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -51,6 +52,7 @@ import java.util.ArrayList; /** Test {@link TransactionExecutor} logic. */ @RunWith(AndroidJUnit4.class) @SmallTest +@Presubmit public class TransactionExecutorTests { private TransactionExecutor mExecutor; @@ -171,7 +173,7 @@ public class TransactionExecutorTests { ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class); IBinder token = mock(IBinder.class); - ClientTransaction transaction = new ClientTransaction(null /* client */, + ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token /* activityToken */); transaction.addCallback(callback1); transaction.addCallback(callback2); @@ -188,10 +190,10 @@ public class TransactionExecutorTests { @Test public void testRequiredStateResolution() { - ActivityResultItem activityResultItem = new ActivityResultItem(new ArrayList<>()); + ActivityResultItem activityResultItem = ActivityResultItem.obtain(new ArrayList<>()); IBinder token = mock(IBinder.class); - ClientTransaction transaction = new ClientTransaction(null /* client */, + ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token /* activityToken */); transaction.addCallback(activityResultItem); diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index f6c656d5f17c..4b1f2dab61b9 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -16,7 +16,9 @@ package android.app.servertransaction; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.app.servertransaction.TestUtils.config; +import static android.app.servertransaction.TestUtils.referrerIntentList; +import static android.app.servertransaction.TestUtils.resultInfoList; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; @@ -57,7 +59,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -77,7 +78,7 @@ public class TransactionParcelTests { @Test public void testConfigurationChange() { // Write to parcel - ConfigurationChangeItem item = new ConfigurationChangeItem(config()); + ConfigurationChangeItem item = ConfigurationChangeItem.obtain(config()); writeAndPrepareForReading(item); // Read from parcel and assert @@ -90,7 +91,7 @@ public class TransactionParcelTests { @Test public void testActivityConfigChange() { // Write to parcel - ActivityConfigurationChangeItem item = new ActivityConfigurationChangeItem(config()); + ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(config()); writeAndPrepareForReading(item); // Read from parcel and assert @@ -104,7 +105,7 @@ public class TransactionParcelTests { @Test public void testMoveToDisplay() { // Write to parcel - MoveToDisplayItem item = new MoveToDisplayItem(4 /* targetDisplayId */, config()); + MoveToDisplayItem item = MoveToDisplayItem.obtain(4 /* targetDisplayId */, config()); writeAndPrepareForReading(item); // Read from parcel and assert @@ -117,7 +118,7 @@ public class TransactionParcelTests { @Test public void testNewIntent() { // Write to parcel - NewIntentItem item = new NewIntentItem(referrerIntentList(), true /* pause */); + NewIntentItem item = NewIntentItem.obtain(referrerIntentList(), true /* pause */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -130,7 +131,7 @@ public class TransactionParcelTests { @Test public void testActivityResult() { // Write to parcel - ActivityResultItem item = new ActivityResultItem(resultInfoList()); + ActivityResultItem item = ActivityResultItem.obtain(resultInfoList()); writeAndPrepareForReading(item); // Read from parcel and assert @@ -143,7 +144,7 @@ public class TransactionParcelTests { @Test public void testPipModeChange() { // Write to parcel - PipModeChangeItem item = new PipModeChangeItem(true /* isInPipMode */, config()); + PipModeChangeItem item = PipModeChangeItem.obtain(true /* isInPipMode */, config()); writeAndPrepareForReading(item); // Read from parcel and assert @@ -156,7 +157,7 @@ public class TransactionParcelTests { @Test public void testMultiWindowModeChange() { // Write to parcel - MultiWindowModeChangeItem item = new MultiWindowModeChangeItem( + MultiWindowModeChangeItem item = MultiWindowModeChangeItem.obtain( true /* isInMultiWindowMode */, config()); writeAndPrepareForReading(item); @@ -171,7 +172,7 @@ public class TransactionParcelTests { @Test public void testWindowVisibilityChange() { // Write to parcel - WindowVisibilityItem item = new WindowVisibilityItem(true /* showWindow */); + WindowVisibilityItem item = WindowVisibilityItem.obtain(true /* showWindow */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -181,7 +182,7 @@ public class TransactionParcelTests { assertTrue(item.equals(result)); // Check different value - item = new WindowVisibilityItem(false); + item = WindowVisibilityItem.obtain(false); mParcel = Parcel.obtain(); writeAndPrepareForReading(item); @@ -195,7 +196,7 @@ public class TransactionParcelTests { @Test public void testDestroy() { - DestroyActivityItem item = new DestroyActivityItem(true /* finished */, + DestroyActivityItem item = DestroyActivityItem.obtain(true /* finished */, 135 /* configChanges */); writeAndPrepareForReading(item); @@ -228,7 +229,7 @@ public class TransactionParcelTests { PersistableBundle persistableBundle = new PersistableBundle(); persistableBundle.putInt("k", 4); - LaunchActivityItem item = new LaunchActivityItem(intent, ident, activityInfo, + LaunchActivityItem item = LaunchActivityItem.obtain(intent, ident, activityInfo, config(), overrideConfig, compat, referrer, null /* voiceInteractor */, procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(), true /* isForward */, null /* profilerInfo */); @@ -244,8 +245,8 @@ public class TransactionParcelTests { @Test public void testPause() { // Write to parcel - PauseActivityItem item = new PauseActivityItem(true /* finished */, true /* userLeaving */, - 135 /* configChanges */, true /* dontReport */); + PauseActivityItem item = PauseActivityItem.obtain(true /* finished */, + true /* userLeaving */, 135 /* configChanges */, true /* dontReport */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -258,7 +259,8 @@ public class TransactionParcelTests { @Test public void testResume() { // Write to parcel - ResumeActivityItem item = new ResumeActivityItem(27 /* procState */, true /* isForward */); + ResumeActivityItem item = ResumeActivityItem.obtain(27 /* procState */, + true /* isForward */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -271,7 +273,8 @@ public class TransactionParcelTests { @Test public void testStop() { // Write to parcel - StopActivityItem item = new StopActivityItem(true /* showWindow */, 14 /* configChanges */); + StopActivityItem item = StopActivityItem.obtain(true /* showWindow */, + 14 /* configChanges */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -284,16 +287,17 @@ public class TransactionParcelTests { @Test public void testClientTransaction() { // Write to parcel - WindowVisibilityItem callback1 = new WindowVisibilityItem(true); - ActivityConfigurationChangeItem callback2 = new ActivityConfigurationChangeItem(config()); + WindowVisibilityItem callback1 = WindowVisibilityItem.obtain(true); + ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain( + config()); - StopActivityItem lifecycleRequest = new StopActivityItem(true /* showWindow */, + StopActivityItem lifecycleRequest = StopActivityItem.obtain(true /* showWindow */, 78 /* configChanges */); IApplicationThread appThread = new StubAppThread(); Binder activityToken = new Binder(); - ClientTransaction transaction = new ClientTransaction(appThread, activityToken); + ClientTransaction transaction = ClientTransaction.obtain(appThread, activityToken); transaction.addCallback(callback1); transaction.addCallback(callback2); transaction.setLifecycleStateRequest(lifecycleRequest); @@ -310,13 +314,14 @@ public class TransactionParcelTests { @Test public void testClientTransactionCallbacksOnly() { // Write to parcel - WindowVisibilityItem callback1 = new WindowVisibilityItem(true); - ActivityConfigurationChangeItem callback2 = new ActivityConfigurationChangeItem(config()); + WindowVisibilityItem callback1 = WindowVisibilityItem.obtain(true); + ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain( + config()); IApplicationThread appThread = new StubAppThread(); Binder activityToken = new Binder(); - ClientTransaction transaction = new ClientTransaction(appThread, activityToken); + ClientTransaction transaction = ClientTransaction.obtain(appThread, activityToken); transaction.addCallback(callback1); transaction.addCallback(callback2); @@ -332,13 +337,13 @@ public class TransactionParcelTests { @Test public void testClientTransactionLifecycleOnly() { // Write to parcel - StopActivityItem lifecycleRequest = new StopActivityItem(true /* showWindow */, + StopActivityItem lifecycleRequest = StopActivityItem.obtain(true /* showWindow */, 78 /* configChanges */); IApplicationThread appThread = new StubAppThread(); Binder activityToken = new Binder(); - ClientTransaction transaction = new ClientTransaction(appThread, activityToken); + ClientTransaction transaction = ClientTransaction.obtain(appThread, activityToken); transaction.setLifecycleStateRequest(lifecycleRequest); writeAndPrepareForReading(transaction); @@ -350,49 +355,6 @@ public class TransactionParcelTests { assertTrue(transaction.equals(result)); } - private static List<ResultInfo> resultInfoList() { - String resultWho1 = "resultWho1"; - int requestCode1 = 7; - int resultCode1 = 4; - Intent data1 = new Intent("action1"); - ResultInfo resultInfo1 = new ResultInfo(resultWho1, requestCode1, resultCode1, data1); - - String resultWho2 = "resultWho2"; - int requestCode2 = 8; - int resultCode2 = 6; - Intent data2 = new Intent("action2"); - ResultInfo resultInfo2 = new ResultInfo(resultWho2, requestCode2, resultCode2, data2); - - List<ResultInfo> resultInfoList = new ArrayList<>(); - resultInfoList.add(resultInfo1); - resultInfoList.add(resultInfo2); - - return resultInfoList; - } - - private static List<ReferrerIntent> referrerIntentList() { - Intent intent1 = new Intent("action1"); - ReferrerIntent referrerIntent1 = new ReferrerIntent(intent1, "referrer1"); - - Intent intent2 = new Intent("action2"); - ReferrerIntent referrerIntent2 = new ReferrerIntent(intent2, "referrer2"); - - List<ReferrerIntent> referrerIntents = new ArrayList<>(); - referrerIntents.add(referrerIntent1); - referrerIntents.add(referrerIntent2); - - return referrerIntents; - } - - private static Configuration config() { - Configuration config = new Configuration(); - config.densityDpi = 10; - config.fontScale = 0.3f; - config.screenHeightDp = 15; - config.orientation = ORIENTATION_LANDSCAPE; - return config; - } - /** Write to {@link #mParcel} and reset its position to prepare for reading from the start. */ private void writeAndPrepareForReading(Parcelable parcelable) { parcelable.writeToParcel(mParcel, 0 /* flags */); diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 77c6c3e1c193..0982a4b58c99 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -194,6 +194,7 @@ public class SettingsBackupTest { Settings.Global.DROPBOX_RESERVE_PERCENT, Settings.Global.DROPBOX_TAG_PREFIX, Settings.Global.EMERGENCY_AFFORDANCE_NEEDED, + Settings.Global.EMULATE_DISPLAY_CUTOUT, Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, Settings.Global.ENABLE_CACHE_QUOTA_CALCULATION, Settings.Global.ENABLE_CELLULAR_ON_BOOT, diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 3eee4da7ba5f..5e7f9c6a40e5 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -337,6 +337,7 @@ <item>com.android.systemui.LatencyTester</item> <item>com.android.systemui.globalactions.GlobalActionsComponent</item> <item>com.android.systemui.RoundedCorners</item> + <item>com.android.systemui.EmulatedDisplayCutout</item> </string-array> <!-- SystemUI vender service, used in config_systemUIServiceComponents. --> diff --git a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java new file mode 100644 index 000000000000..edd1748c7380 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java @@ -0,0 +1,140 @@ +/* + * 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 com.android.systemui; + +import android.content.Context; +import android.database.ContentObserver; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.provider.Settings; +import android.view.DisplayCutout; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.WindowInsets; +import android.view.WindowManager; + +import java.util.ArrayList; + +/** + * Emulates a display cutout by drawing its shape in an overlay as supplied by + * {@link DisplayCutout}. + */ +public class EmulatedDisplayCutout extends SystemUI { + private View mOverlay; + private boolean mAttached; + private WindowManager mWindowManager; + + @Override + public void start() { + mWindowManager = mContext.getSystemService(WindowManager.class); + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.EMULATE_DISPLAY_CUTOUT), + false, mObserver, UserHandle.USER_ALL); + mObserver.onChange(false); + } + + private void setAttached(boolean attached) { + if (attached && !mAttached) { + if (mOverlay == null) { + mOverlay = new CutoutView(mContext); + mOverlay.setLayoutParams(getLayoutParams()); + } + mWindowManager.addView(mOverlay, mOverlay.getLayoutParams()); + mAttached = true; + } else if (!attached && mAttached) { + mWindowManager.removeView(mOverlay); + mAttached = false; + } + } + + private WindowManager.LayoutParams getLayoutParams() { + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_SLIPPERY + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR, + PixelFormat.TRANSLUCENT); + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS + | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; + lp.setTitle("EmulatedDisplayCutout"); + lp.gravity = Gravity.TOP; + return lp; + } + + private ContentObserver mObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + boolean emulateCutout = Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.EMULATE_DISPLAY_CUTOUT, + Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF) + != Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF; + setAttached(emulateCutout); + } + }; + + private static class CutoutView extends View { + private Paint mPaint = new Paint(); + private Path mPath = new Path(); + private ArrayList<Point> mBoundingPolygon = new ArrayList<>(); + + CutoutView(Context context) { + super(context); + } + + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + insets.getDisplayCutout().getBoundingPolygon(mBoundingPolygon); + invalidate(); + return insets.consumeCutout(); + } + + @Override + protected void onDraw(Canvas canvas) { + if (!mBoundingPolygon.isEmpty()) { + mPaint.setColor(Color.DKGRAY); + mPaint.setStyle(Paint.Style.FILL); + + mPath.reset(); + for (int i = 0; i < mBoundingPolygon.size(); i++) { + Point point = mBoundingPolygon.get(i); + if (i == 0) { + mPath.moveTo(point.x, point.y); + } else { + mPath.lineTo(point.x, point.y); + } + } + mPath.close(); + canvas.drawPath(mPath, mPaint); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 06d774916ad7..3538327130d4 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -37,9 +37,7 @@ import com.android.systemui.keyboard.KeyboardUI; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.media.RingtonePlayer; import com.android.systemui.pip.PipUI; -import com.android.systemui.plugins.GlobalActions; import com.android.systemui.plugins.OverlayPlugin; -import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginManager; import com.android.systemui.power.PowerUI; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 982380c8b85c..5d6cf749bd1c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -20680,7 +20680,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc " + app.processName + " new config " + configCopy); mLifecycleManager.scheduleTransaction(app.thread, - new ConfigurationChangeItem(configCopy)); + ConfigurationChangeItem.obtain(configCopy)); } } catch (Exception e) { Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e); diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index a089e6ceef78..60a623677825 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -618,7 +618,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo + ", displayId=" + displayId + ", config=" + config); service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new MoveToDisplayItem(displayId, config)); + MoveToDisplayItem.obtain(displayId, config)); } catch (RemoteException e) { // If process died, whatever. } @@ -636,7 +636,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo + config); service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new ActivityConfigurationChangeItem(config)); + ActivityConfigurationChangeItem.obtain(config)); } catch (RemoteException e) { // If process died, whatever. } @@ -663,7 +663,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private void scheduleMultiWindowModeChanged(Configuration overrideConfig) { try { service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new MultiWindowModeChangeItem(mLastReportedMultiWindowMode, + MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig)); } catch (Exception e) { // If process died, I don't care. @@ -691,7 +691,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private void schedulePictureInPictureModeChanged(Configuration overrideConfig) { try { service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new PipModeChangeItem(mLastReportedPictureInPictureMode, + PipModeChangeItem.obtain(mLastReportedPictureInPictureMode, overrideConfig)); } catch (Exception e) { // If process died, no one cares. @@ -1380,7 +1380,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo ArrayList<ReferrerIntent> ar = new ArrayList<>(1); ar.add(rintent); service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new NewIntentItem(ar, state == PAUSED)); + NewIntentItem.obtain(ar, state == PAUSED)); unsent = false; } catch (RemoteException e) { Slog.w(TAG, "Exception thrown sending new intent to " + this, e); @@ -1603,7 +1603,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo sleeping = false; app.pendingUiClean = true; service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new WindowVisibilityItem(true /* showWindow */)); + WindowVisibilityItem.obtain(true /* showWindow */)); // The activity may be waiting for stop, but that is no longer appropriate for it. mStackSupervisor.mStoppingActivities.remove(this); mStackSupervisor.mGoingToSleepActivities.remove(this); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index fcf99465d1b4..cf40be53e70d 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1434,7 +1434,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mService.updateUsageStats(prev, false); mService.mLifecycleManager.scheduleTransaction(prev.app.thread, prev.appToken, - new PauseActivityItem(prev.finishing, userLeaving, + PauseActivityItem.obtain(prev.finishing, userLeaving, prev.configChangeFlags, pauseImmediately)); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. @@ -2065,7 +2065,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Scheduling invisibility: " + r); mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken, - new WindowVisibilityItem(false /* showWindow */)); + WindowVisibilityItem.obtain(false /* showWindow */)); } // Reset the flag indicating that an app can enter picture-in-picture once the @@ -2593,13 +2593,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a); mService.mLifecycleManager.scheduleTransaction(next.app.thread, - next.appToken, new ActivityResultItem(a)); + next.appToken, ActivityResultItem.obtain(a)); } } if (next.newIntents != null) { mService.mLifecycleManager.scheduleTransaction(next.app.thread, - next.appToken, new NewIntentItem(next.newIntents, + next.appToken, NewIntentItem.obtain(next.newIntents, false /* andPause */)); } @@ -2618,7 +2618,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.app.forceProcessStateUpTo(mService.mTopProcessState); next.clearOptionsLocked(); mService.mLifecycleManager.scheduleTransaction(next.app.thread, next.appToken, - new ResumeActivityItem(next.app.repProcState, + ResumeActivityItem.obtain(next.app.repProcState, mService.isNextTransitionForward())); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed " @@ -3270,7 +3270,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai list.add(new ResultInfo(resultWho, requestCode, resultCode, data)); mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken, - new ActivityResultItem(list)); + ActivityResultItem.obtain(list)); return; } catch (Exception e) { Slog.w(TAG, "Exception thrown sending result to " + r, e); @@ -3399,7 +3399,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai EventLogTags.writeAmStopActivity( r.userId, System.identityHashCode(r), r.shortComponentName); mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken, - new StopActivityItem(r.visible, r.configChangeFlags)); + StopActivityItem.obtain(r.visible, r.configChangeFlags)); if (shouldSleepOrShutDownActivities()) { r.setSleeping(true); } @@ -4205,7 +4205,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai try { if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r); mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken, - new DestroyActivityItem(r.finishing, r.configChangeFlags)); + DestroyActivityItem.obtain(r.finishing, r.configChangeFlags)); } catch (Exception e) { // We can just ignore exceptions here... if the process // has crashed, our death notification will clean things diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 7561d0f8ad96..edaa51112867 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1239,9 +1239,16 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) { synchronized (mService) { - return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType, - PackageManager.MATCH_INSTANT | PackageManager.MATCH_DEFAULT_ONLY | flags - | ActivityManagerService.STOCK_PM_FLAGS, userId, true); + try { + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent"); + return mService.getPackageManagerInternalLocked().resolveIntent( + intent, resolvedType, PackageManager.MATCH_INSTANT + | PackageManager.MATCH_DEFAULT_ONLY | flags + | ActivityManagerService.STOCK_PM_FLAGS, userId, true); + + } finally { + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + } } } @@ -1400,9 +1407,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Create activity launch transaction. - final ClientTransaction clientTransaction = new ClientTransaction(app.thread, + final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread, r.appToken); - clientTransaction.addCallback(new LaunchActivityItem(new Intent(r.intent), + clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global // and override configs. @@ -1415,9 +1422,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = new ResumeActivityItem(mService.isNextTransitionForward()); + lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward()); } else { - lifecycleItem = new PauseActivityItem(); + lifecycleItem = PauseActivityItem.obtain(); } clientTransaction.setLifecycleStateRequest(lifecycleItem); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index dda8e9c18229..10fb6e2a0896 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -96,6 +96,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.service.voice.IVoiceInteractionSession; diff --git a/services/core/java/com/android/server/am/ClientLifecycleManager.java b/services/core/java/com/android/server/am/ClientLifecycleManager.java index c04d103c7ff5..cc70f18dc747 100644 --- a/services/core/java/com/android/server/am/ClientLifecycleManager.java +++ b/services/core/java/com/android/server/am/ClientLifecycleManager.java @@ -43,6 +43,7 @@ class ClientLifecycleManager { */ void scheduleTransaction(ClientTransaction transaction) throws RemoteException { transaction.schedule(); + transaction.recycle(); } /** @@ -100,7 +101,7 @@ class ClientLifecycleManager { */ private static ClientTransaction transactionWithState(@NonNull IApplicationThread client, @NonNull IBinder activityToken, @NonNull ActivityLifecycleItem stateRequest) { - final ClientTransaction clientTransaction = new ClientTransaction(client, activityToken); + final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken); clientTransaction.setLifecycleStateRequest(stateRequest); return clientTransaction; } @@ -113,7 +114,7 @@ class ClientLifecycleManager { */ private static ClientTransaction transactionWithCallback(@NonNull IApplicationThread client, IBinder activityToken, @NonNull ClientTransactionItem callback) { - final ClientTransaction clientTransaction = new ClientTransaction(client, activityToken); + final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken); clientTransaction.addCallback(callback); return clientTransaction; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f60cb06a7756..e0f3ec7453a9 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -607,6 +607,12 @@ public class PackageManagerService extends IPackageManager.Stub */ private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true; + /** + * Permissions required in order to receive instant application lifecycle broadcasts. + */ + private static final String[] INSTANT_APP_BROADCAST_PERMISSION = + new String[] { android.Manifest.permission.ACCESS_INSTANT_APPS }; + final ServiceThread mHandlerThread; final PackageHandler mHandler; @@ -1967,16 +1973,20 @@ public class PackageManagerService extends IPackageManager.Stub // Determine the set of users who are adding this package for // the first time vs. those who are seeing an update. - int[] firstUsers = EMPTY_INT_ARRAY; - int[] updateUsers = EMPTY_INT_ARRAY; + int[] firstUserIds = EMPTY_INT_ARRAY; + int[] firstInstantUserIds = EMPTY_INT_ARRAY; + int[] updateUserIds = EMPTY_INT_ARRAY; + int[] instantUserIds = EMPTY_INT_ARRAY; final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0; final PackageSetting ps = (PackageSetting) res.pkg.mExtras; for (int newUser : res.newUsers) { - if (ps.getInstantApp(newUser)) { - continue; - } + final boolean isInstantApp = ps.getInstantApp(newUser); if (allNewUsers) { - firstUsers = ArrayUtils.appendInt(firstUsers, newUser); + if (isInstantApp) { + firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser); + } else { + firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser); + } continue; } boolean isNew = true; @@ -1987,9 +1997,17 @@ public class PackageManagerService extends IPackageManager.Stub } } if (isNew) { - firstUsers = ArrayUtils.appendInt(firstUsers, newUser); + if (isInstantApp) { + firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser); + } else { + firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser); + } } else { - updateUsers = ArrayUtils.appendInt(updateUsers, newUser); + if (isInstantApp) { + instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser); + } else { + updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser); + } } } @@ -2002,7 +2020,7 @@ public class PackageManagerService extends IPackageManager.Stub int appId = UserHandle.getAppId(res.uid); boolean isSystem = res.pkg.applicationInfo.isSystemApp(); sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload, - virtualPreload /*startReceiver*/, appId, firstUsers); + virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds); // Send added for users that don't see the package for the first time Bundle extras = new Bundle(1); @@ -2012,11 +2030,13 @@ public class PackageManagerService extends IPackageManager.Stub } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, - null /*targetPackage*/, null /*finishedReceiver*/, updateUsers); + null /*targetPackage*/, null /*finishedReceiver*/, + updateUserIds, instantUserIds); if (installerPackageName != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, - installerPackageName, null /*finishedReceiver*/, updateUsers); + installerPackageName, null /*finishedReceiver*/, + updateUserIds, instantUserIds); } // Send replaced for users that don't see the package for the first time @@ -2024,24 +2044,26 @@ public class PackageManagerService extends IPackageManager.Stub sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, 0 /*flags*/, null /*targetPackage*/, null /*finishedReceiver*/, - updateUsers); + updateUserIds, instantUserIds); if (installerPackageName != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, 0 /*flags*/, - installerPackageName, null /*finishedReceiver*/, updateUsers); + installerPackageName, null /*finishedReceiver*/, + updateUserIds, instantUserIds); } sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null /*package*/, null /*extras*/, 0 /*flags*/, packageName /*targetPackage*/, - null /*finishedReceiver*/, updateUsers); + null /*finishedReceiver*/, updateUserIds, instantUserIds); } else if (launchedForRestore && !isSystemApp(res.pkg)) { // First-install and we did a restore, so we're responsible for the // first-launch broadcast. if (DEBUG_BACKUP) { Slog.i(TAG, "Post-restore of " + packageName - + " sending FIRST_LAUNCH in " + Arrays.toString(firstUsers)); + + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds)); } - sendFirstLaunchBroadcast(packageName, installerPackage, firstUsers); + sendFirstLaunchBroadcast(packageName, installerPackage, + firstUserIds, firstInstantUserIds); } // Send broadcast package appeared if forward locked/external for all users @@ -2059,9 +2081,9 @@ public class PackageManagerService extends IPackageManager.Stub } // Work that needs to happen on first install within each user - if (firstUsers != null && firstUsers.length > 0) { + if (firstUserIds != null && firstUserIds.length > 0) { synchronized (mPackages) { - for (int userId : firstUsers) { + for (int userId : firstUserIds) { // If this app is a browser and it's newly-installed for some // users, clear any default-browser state in those users. The // app's nature doesn't depend on the user, so we can just check @@ -2099,7 +2121,7 @@ public class PackageManagerService extends IPackageManager.Stub // should not change. // Don't notify the manager for ephemeral apps as they are not expected to // survive long enough to benefit of background optimizations. - for (int userId : firstUsers) { + for (int userId : firstUserIds) { PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId); // There's a race currently where some install events may interleave with an uninstall. // This can lead to package info being null (b/36642664). @@ -12777,9 +12799,10 @@ public class PackageManagerService extends IPackageManager.Stub } }; + @Override public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, final int flags, final String targetPkg, final IIntentReceiver finishedReceiver, - final int[] userIds) { + final int[] userIds, int[] instantUserIds) { mHandler.post(new Runnable() { @Override public void run() { @@ -12792,33 +12815,11 @@ public class PackageManagerService extends IPackageManager.Stub } else { resolvedUserIds = userIds; } - for (int id : resolvedUserIds) { - final Intent intent = new Intent(action, - pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null); - if (extras != null) { - intent.putExtras(extras); - } - if (targetPkg != null) { - intent.setPackage(targetPkg); - } - // Modify the UID when posting to other users - int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); - if (uid > 0 && UserHandle.getUserId(uid) != id) { - uid = UserHandle.getUid(id, UserHandle.getAppId(uid)); - intent.putExtra(Intent.EXTRA_UID, uid); - } - intent.putExtra(Intent.EXTRA_USER_HANDLE, id); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags); - if (DEBUG_BROADCASTS) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.d(TAG, "Sending to user " + id + ": " - + intent.toShortString(false, true, false, false) - + " " + intent.getExtras(), here); - } - am.broadcastIntent(null, intent, null, finishedReceiver, - 0, null, null, null, android.app.AppOpsManager.OP_NONE, - null, finishedReceiver != null, false, id); + doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver, + resolvedUserIds, false); + if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) { + doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver, + instantUserIds, true); } } catch (RemoteException ex) { } @@ -12827,6 +12828,49 @@ public class PackageManagerService extends IPackageManager.Stub } /** + * Sends a broadcast for the given action. + * <p>If {@code isInstantApp} is {@code true}, then the broadcast is protected with + * the {@link android.Manifest.permission#ACCESS_INSTANT_APPS} permission. This allows + * the system and applications allowed to see instant applications to receive package + * lifecycle events for instant applications. + */ + private void doSendBroadcast(IActivityManager am, String action, String pkg, Bundle extras, + int flags, String targetPkg, IIntentReceiver finishedReceiver, + int[] userIds, boolean isInstantApp) + throws RemoteException { + for (int id : userIds) { + final Intent intent = new Intent(action, + pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null); + final String[] requiredPermissions = + isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null; + if (extras != null) { + intent.putExtras(extras); + } + if (targetPkg != null) { + intent.setPackage(targetPkg); + } + // Modify the UID when posting to other users + int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (uid > 0 && UserHandle.getUserId(uid) != id) { + uid = UserHandle.getUid(id, UserHandle.getAppId(uid)); + intent.putExtra(Intent.EXTRA_UID, uid); + } + intent.putExtra(Intent.EXTRA_USER_HANDLE, id); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags); + if (DEBUG_BROADCASTS) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "Sending to user " + id + ": " + + intent.toShortString(false, true, false, false) + + " " + intent.getExtras(), here); + } + am.broadcastIntent(null, intent, null, finishedReceiver, + 0, null, null, requiredPermissions, android.app.AppOpsManager.OP_NONE, + null, finishedReceiver != null, false, id); + } + } + + /** * Check if the external storage media is available. This is true if there * is a mounted external storage medium or if the external storage is * emulated. @@ -13075,8 +13119,11 @@ public class PackageManagerService extends IPackageManager.Stub private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting, int userId) { final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting); + final boolean isInstantApp = pkgSetting.getInstantApp(userId); + final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; + final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; sendPackageAddedForNewUsers(packageName, isSystem /*sendBootCompleted*/, - false /*startReceiver*/, pkgSetting.appId, userId); + false /*startReceiver*/, pkgSetting.appId, userIds, instantUserIds); // Send a session commit broadcast final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo(); @@ -13085,18 +13132,21 @@ public class PackageManagerService extends IPackageManager.Stub sendSessionCommitBroadcast(info, userId); } + @Override public void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted, - boolean includeStopped, int appId, int... userIds) { - if (ArrayUtils.isEmpty(userIds)) { + boolean includeStopped, int appId, int[] userIds, int[] instantUserIds) { + if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) { return; } Bundle extras = new Bundle(1); // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast - extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userIds[0], appId)); + final int uid = UserHandle.getUid( + (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId); + extras.putInt(Intent.EXTRA_UID, uid); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, - packageName, extras, 0, null, null, userIds); - if (sendBootCompleted) { + packageName, extras, 0, null, null, userIds, instantUserIds); + if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) { mHandler.post(() -> { for (int userId : userIds) { sendBootCompletedBroadcastToSystemApp( @@ -13240,7 +13290,7 @@ public class PackageManagerService extends IPackageManager.Stub suspended ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED, null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, - new int[] {userId}); + new int[] {userId}, null); } } @@ -14128,7 +14178,8 @@ public class PackageManagerService extends IPackageManager.Stub * the first-launch broadcast will be sent implicitly on that basis in POST_INSTALL * handling. */ - void notifyFirstLaunch(final String pkgName, final String installerPackage, final int userId) { + void notifyFirstLaunch(final String packageName, final String installerPackage, + final int userId) { // Serialize this with the rest of the install-process message chain. In the // restore-at-install case, this Runnable will necessarily run before the // POST_INSTALL message is processed, so the contents of mRunningInstalls @@ -14143,12 +14194,12 @@ public class PackageManagerService extends IPackageManager.Stub if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) { continue; } - if (pkgName.equals(data.res.pkg.applicationInfo.packageName)) { + if (packageName.equals(data.res.pkg.applicationInfo.packageName)) { // right package; but is it for the right user? for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) { if (userId == data.res.newUsers[uIndex]) { if (DEBUG_BACKUP) { - Slog.i(TAG, "Package " + pkgName + Slog.i(TAG, "Package " + packageName + " being restored so deferring FIRST_LAUNCH"); } return; @@ -14158,16 +14209,20 @@ public class PackageManagerService extends IPackageManager.Stub } // didn't find it, so not being restored if (DEBUG_BACKUP) { - Slog.i(TAG, "Package " + pkgName + " sending normal FIRST_LAUNCH"); + Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH"); } - sendFirstLaunchBroadcast(pkgName, installerPackage, new int[] {userId}); + final boolean isInstantApp = isInstantApp(packageName, userId); + final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; + final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; + sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds); } }); } - private void sendFirstLaunchBroadcast(String pkgName, String installerPkg, int[] userIds) { + private void sendFirstLaunchBroadcast(String pkgName, String installerPkg, + int[] userIds, int[] instantUserIds) { sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0, - installerPkg, null, userIds); + installerPkg, null, userIds, instantUserIds); } private abstract class HandlerParams { @@ -17314,6 +17369,7 @@ public class PackageManagerService extends IPackageManager.Stub int[] origUsers; int[] removedUsers = null; int[] broadcastUsers = null; + int[] instantUserIds = null; SparseArray<Integer> installReasons; boolean isRemovedPackageSystemUpdate = false; boolean isUpdate; @@ -17359,7 +17415,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageInstalledInfo installedInfo = appearedChildPackages.valueAt(i); packageSender.sendPackageAddedForNewUsers(installedInfo.name, true /*sendBootCompleted*/, false /*startReceiver*/, - UserHandle.getAppId(installedInfo.uid), installedInfo.newUsers); + UserHandle.getAppId(installedInfo.uid), installedInfo.newUsers, null); } } @@ -17368,18 +17424,18 @@ public class PackageManagerService extends IPackageManager.Stub extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); extras.putBoolean(Intent.EXTRA_REPLACING, true); packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, - removedPackage, extras, 0, null /*targetPackage*/, null, null); + removedPackage, extras, 0, null /*targetPackage*/, null, null, null); packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, - removedPackage, extras, 0, null /*targetPackage*/, null, null); + removedPackage, extras, 0, null /*targetPackage*/, null, null, null); packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, - null, null, 0, removedPackage, null, null); + null, null, 0, removedPackage, null, null, null); if (installerPackageName != null) { packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras, 0 /*flags*/, - installerPackageName, null, null); + installerPackageName, null, null, null); packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, removedPackage, extras, 0 /*flags*/, - installerPackageName, null, null); + installerPackageName, null, null, null); } } @@ -17400,23 +17456,24 @@ public class PackageManagerService extends IPackageManager.Stub extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers); if (removedPackage != null) { packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, - removedPackage, extras, 0, null /*targetPackage*/, null, broadcastUsers); + removedPackage, extras, 0, null /*targetPackage*/, null, + broadcastUsers, instantUserIds); if (installerPackageName != null) { packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras, 0 /*flags*/, - installerPackageName, null, broadcastUsers); + installerPackageName, null, broadcastUsers, instantUserIds); } if (dataRemoved && !isRemovedPackageSystemUpdate) { packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, - null, null, broadcastUsers); + null, null, broadcastUsers, instantUserIds); } } if (removedAppId >= 0) { packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, - null, null, broadcastUsers); + null, null, broadcastUsers, instantUserIds); } } @@ -17428,12 +17485,14 @@ public class PackageManagerService extends IPackageManager.Stub } broadcastUsers = EMPTY_INT_ARRAY; + instantUserIds = EMPTY_INT_ARRAY; for (int i = userIds.length - 1; i >= 0; --i) { final int userId = userIds[i]; if (deletedPackageSetting.getInstantApp(userId)) { - continue; + instantUserIds = ArrayUtils.appendInt(instantUserIds, userId); + } else { + broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId); } - broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId); } } } @@ -19979,8 +20038,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); // little component state change. final int flags = !componentNames.contains(packageName) ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0; + final int userId = UserHandle.getUserId(packageUid); + final boolean isInstantApp = isInstantApp(packageName, userId); + final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; + final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null, - new int[] {UserHandle.getUserId(packageUid)}); + userIds, instantUserIds); } @Override @@ -21099,7 +21162,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; - sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null); + sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null, null); } } @@ -23328,9 +23391,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } interface PackageSender { + /** + * @param userIds User IDs where the action occurred on a full application + * @param instantUserIds User IDs where the action occurred on an instant application + */ void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, final int flags, final String targetPkg, - final IIntentReceiver finishedReceiver, final int[] userIds); + final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds); void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted, - boolean includeStopped, int appId, int... userIds); + boolean includeStopped, int appId, int[] userIds, int[] instantUserIds); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 9a7e72a0455a..61591bbef08d 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -48,6 +48,7 @@ import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA; import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN; @@ -607,6 +608,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { PointerLocationView mPointerLocationView; + boolean mEmulateDisplayCutout = false; + // During layout, the layer at which the doc window is placed. int mDockLayer; // During layout, this is the layer of the status bar. @@ -965,6 +968,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.POLICY_CONTROL), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.EMULATE_DISPLAY_CUTOUT), false, this, + UserHandle.USER_ALL); updateSettings(); } @@ -2396,6 +2402,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mImmersiveModeConfirmation != null) { mImmersiveModeConfirmation.loadSetting(mCurrentUserId); } + mEmulateDisplayCutout = Settings.Global.getInt(resolver, + Settings.Global.EMULATE_DISPLAY_CUTOUT, + Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF) + != Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF; } synchronized (mWindowManagerFuncs.getWindowManagerLock()) { PolicyControl.reloadFromSetting(mContext); @@ -4436,7 +4446,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { - displayFrames.onBeginLayout(); + displayFrames.onBeginLayout(mEmulateDisplayCutout, mStatusBarHeight); // TODO(multi-display): This doesn't seem right...Maybe only apply to default display? mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); @@ -4506,6 +4516,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } layoutScreenDecorWindows(displayFrames, pf, df, dcf); + + if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { + // Make sure that the zone we're avoiding for the cutout is at least as tall as the + // status bar; otherwise fullscreen apps will end up cutting halfway into the status + // bar. + displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top, + displayFrames.mStable.top); + } } private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) { @@ -4641,11 +4659,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { final Rect dockFrame = displayFrames.mDock; mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation); + final Rect cutoutSafeUnrestricted = mTmpRect; + cutoutSafeUnrestricted.set(displayFrames.mUnrestricted); + cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + if (mNavigationBarPosition == NAV_BAR_BOTTOM) { // It's a system nav bar or a portrait screen; nav bar goes on bottom. - final int top = displayFrames.mUnrestricted.bottom + final int top = cutoutSafeUnrestricted.bottom - getNavigationBarHeight(rotation, uiMode); - mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom); + mTmpNavigationFrame.set(0, top, displayWidth, cutoutSafeUnrestricted.bottom); displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); @@ -4666,9 +4688,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { // Landscape screen; nav bar goes to the right. - final int left = displayFrames.mUnrestricted.right + final int left = cutoutSafeUnrestricted.right - getNavigationBarWidth(rotation, uiMode); - mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight); + mTmpNavigationFrame.set(left, 0, cutoutSafeUnrestricted.right, displayHeight); displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); @@ -4689,9 +4711,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } else if (mNavigationBarPosition == NAV_BAR_LEFT) { // Seascape screen; nav bar goes to the left. - final int right = displayFrames.mUnrestricted.left + final int right = cutoutSafeUnrestricted.left + getNavigationBarWidth(rotation, uiMode); - mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight); + mTmpNavigationFrame.set(cutoutSafeUnrestricted.left, 0, right, displayHeight); displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); @@ -4843,6 +4865,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int type = attrs.type; final int fl = PolicyControl.getWindowFlags(win, attrs); + final long fl2 = attrs.flags2; final int pfl = attrs.privateFlags; final int sim = attrs.softInputMode; final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(win, null); @@ -4863,6 +4886,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int adjust = sim & SOFT_INPUT_MASK_ADJUST; + final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 + || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0 + || (requestedSysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0; + + final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN; + final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; + final boolean layoutInCutout = (fl2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0; + sf.set(displayFrames.mStable); if (type == TYPE_INPUT_METHOD) { @@ -4872,7 +4903,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { df.set(displayFrames.mDock); pf.set(displayFrames.mDock); // IM dock windows layout below the nav bar... - pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom; + pf.bottom = df.bottom = of.bottom = Math.min(displayFrames.mUnrestricted.bottom, + displayFrames.mDisplayCutoutSafe.bottom); // ...with content insets above the nav bar cf.bottom = vf.bottom = displayFrames.mStable.bottom; if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) { @@ -4943,8 +4975,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) - == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { + if (layoutInScreen && layoutInsetDecor) { if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN, INSET_DECOR"); // This is the case for a normal activity window: we want it to cover all of the @@ -5021,6 +5052,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // moving from a window that is not hiding the status bar to one that is. cf.set(displayFrames.mRestricted); } + if (requestedFullscreen && !layoutInCutout) { + pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + } applyStableConstraints(sysUiFl, fl, cf, displayFrames); if (adjust != SOFT_INPUT_ADJUST_NOTHING) { vf.set(displayFrames.mCurrent); @@ -5028,7 +5062,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { vf.set(cf); } } - } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl + } else if (layoutInScreen || (sysUiFl & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) { if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() @@ -5106,6 +5140,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { of.set(displayFrames.mUnrestricted); df.set(displayFrames.mUnrestricted); pf.set(displayFrames.mUnrestricted); + if (requestedFullscreen && !layoutInCutout) { + pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + } } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { of.set(displayFrames.mRestricted); df.set(displayFrames.mRestricted); @@ -5176,9 +5213,27 @@ public class PhoneWindowManager implements WindowManagerPolicy { vf.set(cf); } } + pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); } } + // Ensure that windows that did not request to be laid out in the cutout don't get laid + // out there. + if (!layoutInCutout) { + final Rect displayCutoutSafeExceptMaybeTop = mTmpRect; + displayCutoutSafeExceptMaybeTop.set(displayFrames.mDisplayCutoutSafe); + if (layoutInScreen && layoutInsetDecor) { + // At the top we have the status bar, so apps that are + // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR already expect that there's an inset + // there and we don't need to exclude the window from that area. + displayCutoutSafeExceptMaybeTop.top = Integer.MIN_VALUE; + } + pf.intersectUnchecked(displayCutoutSafeExceptMaybeTop); + } + + // Content should never appear in the cutout. + cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it. // Also, we don't allow windows in multi-window mode to extend out of the screen. if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ca8ffca00ee7..91cad469289a 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -135,6 +135,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; +import android.os.Trace; import android.util.DisplayMetrics; import android.util.MutableBoolean; import android.util.Slog; @@ -178,20 +179,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** The containers below are the only child containers the display can have. */ // Contains all window containers that are related to apps (Activities) - private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(); + private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mService); // Contains all non-app window containers that should be displayed above the app containers // (e.g. Status bar) private final AboveAppWindowContainers mAboveAppWindowsContainers = - new AboveAppWindowContainers("mAboveAppWindowsContainers"); + new AboveAppWindowContainers("mAboveAppWindowsContainers", mService); // Contains all non-app window containers that should be displayed below the app containers // (e.g. Wallpaper). private final NonAppWindowContainers mBelowAppWindowsContainers = - new NonAppWindowContainers("mBelowAppWindowsContainers"); + new NonAppWindowContainers("mBelowAppWindowsContainers", mService); // Contains all IME window containers. Note that the z-ordering of the IME windows will depend // on the IME target. We mainly have this container grouping so we can keep track of all the IME // window containers together and move them in-sync if/when needed. private final NonAppWindowContainers mImeWindowsContainers = - new NonAppWindowContainers("mImeWindowsContainers"); + new NonAppWindowContainers("mImeWindowsContainers", mService); private WindowState mTmpWindow; private WindowState mTmpWindow2; @@ -317,8 +318,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** Used for handing back size of display */ private final Rect mTmpBounds = new Rect(); - WindowManagerService mService; - /** Remove this display when animation on it has completed. */ private boolean mDeferredRemoval; @@ -765,6 +764,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ DisplayContent(Display display, WindowManagerService service, WallpaperController wallpaperController) { + super(service); if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) { throw new IllegalArgumentException("Display with ID=" + display.getDisplayId() + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId()) @@ -777,7 +777,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo display.getDisplayInfo(mDisplayInfo); display.getMetrics(mDisplayMetrics); isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY; - mService = service; mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo); initializeDisplayBaseInfo(); mDividerControllerLocked = new DockedStackDividerController(service, this); @@ -2339,6 +2338,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** Updates the layer assignment of windows on this display. */ void assignWindowLayers(boolean setLayoutNeeded) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers"); assignChildLayers(getPendingTransaction()); if (setLayoutNeeded) { setLayoutNeeded(); @@ -2349,6 +2349,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // prepareSurfaces. This allows us to synchronize Z-ordering changes with // the hiding and showing of surfaces. scheduleAnimation(); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } // TODO: This should probably be called any time a visual change is made to the hierarchy like @@ -3182,6 +3183,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ static class DisplayChildWindowContainer<E extends WindowContainer> extends WindowContainer<E> { + DisplayChildWindowContainer(WindowManagerService service) { + super(service); + } + @Override boolean fillsParent() { return true; @@ -3209,6 +3214,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private TaskStack mPinnedStack = null; private TaskStack mSplitScreenPrimaryStack = null; + TaskStackContainers(WindowManagerService service) { + super(service); + } + /** * Returns the topmost stack on the display that is compatible with the input windowing mode * and activity type. Null is no compatible stack on the display. @@ -3516,35 +3525,37 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void assignChildLayers(SurfaceControl.Transaction t) { - final int NORMAL_STACK_STATE = 0; - final int BOOSTED_STATE = 1; - final int ALWAYS_ON_TOP_STATE = 2; + int layer = 0; // We allow stacks to change visual order from the AM specified order due to // Z-boosting during animations. However we must take care to ensure TaskStacks // which are marked as alwaysOnTop remain that way. - int layer = 0; - for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) { - for (int i = 0; i < mChildren.size(); i++) { - final TaskStack s = mChildren.get(i); - layer++; - if (state == NORMAL_STACK_STATE) { - s.assignLayer(t, layer); - } else if (state == BOOSTED_STATE && s.needsZBoost()) { - s.assignLayer(t, layer); - } else if (state == ALWAYS_ON_TOP_STATE && - s.isAlwaysOnTop()) { - s.assignLayer(t, layer); - } - s.assignChildLayers(t); + for (int i = 0; i < mChildren.size(); i++) { + final TaskStack s = mChildren.get(i); + s.assignChildLayers(); + if (!s.needsZBoost() && !s.isAlwaysOnTop()) { + s.assignLayer(t, layer++); + } + } + for (int i = 0; i < mChildren.size(); i++) { + final TaskStack s = mChildren.get(i); + if (s.needsZBoost() && !s.isAlwaysOnTop()) { + s.assignLayer(t, layer++); } - // The appropriate place for App-Transitions to occur is right - // above all other animations but still below things in the Picture-and-Picture - // windowing mode. - if (state == BOOSTED_STATE && mAnimationLayer != null) { - t.setLayer(mAnimationLayer, layer + 1); + } + for (int i = 0; i < mChildren.size(); i++) { + final TaskStack s = mChildren.get(i); + if (s.isAlwaysOnTop()) { + s.assignLayer(t, layer++); } } + + // The appropriate place for App-Transitions to occur is right + // above all other animations but still below things in the Picture-and-Picture + // windowing mode. + if (mAnimationLayer != null) { + t.setLayer(mAnimationLayer, layer++); + } } @Override @@ -3560,8 +3571,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private final class AboveAppWindowContainers extends NonAppWindowContainers { - AboveAppWindowContainers(String name) { - super(name); + AboveAppWindowContainers(String name, WindowManagerService service) { + super(name, service); } void assignChildLayers(SurfaceControl.Transaction t, WindowContainer imeContainer) { @@ -3577,14 +3588,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (needAssignIme && layer >= mService.mPolicy.getWindowLayerFromTypeLw( TYPE_INPUT_METHOD_DIALOG, true)) { - t.setRelativeLayer(imeContainer.getSurfaceControl(), - wt.getSurfaceControl(), -1); + imeContainer.assignRelativeLayer(t, wt.getSurfaceControl(), -1); needAssignIme = false; } } if (needAssignIme) { - t.setRelativeLayer(imeContainer.getSurfaceControl(), - getSurfaceControl(), Integer.MAX_VALUE); + imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE); } } } @@ -3618,7 +3627,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo }; private final String mName; - NonAppWindowContainers(String name) { + NonAppWindowContainers(String name, WindowManagerService service) { + super(service); mName = name; } @@ -3712,8 +3722,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void assignChildLayers(SurfaceControl.Transaction t) { - t.setLayer(mOverlayLayer, 1) - .setLayer(mWindowingLayer, 0); // These are layers as children of "mWindowingLayer" mBelowAppWindowsContainers.assignLayer(t, 0); @@ -3737,8 +3745,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // place it in the AboveAppWindowContainers. if (imeTarget != null && !imeTarget.inSplitScreenWindowingMode() && (imeTarget.getSurfaceControl() != null)) { - t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(), - imeTarget.getSurfaceControl(), + mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(), // TODO: We need to use an extra level on the app surface to ensure // this is always above SurfaceView but always below attached window. 1); diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java index 209ce3fcfdb4..015571255f0d 100644 --- a/services/core/java/com/android/server/wm/DisplayFrames.java +++ b/services/core/java/com/android/server/wm/DisplayFrames.java @@ -22,12 +22,16 @@ import static android.view.Surface.ROTATION_90; import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS; import android.annotation.NonNull; +import android.graphics.Point; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; import android.view.DisplayCutout; import android.view.DisplayInfo; +import com.android.internal.annotations.VisibleForTesting; + import java.io.PrintWriter; +import java.util.Arrays; /** * Container class for all the display frames that affect how we do window layout on a display. @@ -124,7 +128,7 @@ public class DisplayFrames { info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom); } - public void onBeginLayout() { + public void onBeginLayout(boolean emulateDisplayCutout, int statusBarHeight) { switch (mRotation) { case ROTATION_90: mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top; @@ -165,12 +169,64 @@ public class DisplayFrames { mDisplayCutout = DisplayCutout.NO_CUTOUT; mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); + if (emulateDisplayCutout) { + setEmulatedDisplayCutout((int) (statusBarHeight * 0.8)); + } } public int getInputMethodWindowVisibleHeight() { return mDock.bottom - mCurrent.bottom; } + private void setEmulatedDisplayCutout(int height) { + final boolean swappedDimensions = mRotation == ROTATION_90 || mRotation == ROTATION_270; + + final int screenWidth = swappedDimensions ? mDisplayHeight : mDisplayWidth; + final int screenHeight = swappedDimensions ? mDisplayWidth : mDisplayHeight; + + final int widthTop = (int) (screenWidth * 0.3); + final int widthBottom = widthTop - height; + + switch (mRotation) { + case ROTATION_90: + mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( + new Point(0, (screenWidth - widthTop) / 2), + new Point(height, (screenWidth - widthBottom) / 2), + new Point(height, (screenWidth + widthBottom) / 2), + new Point(0, (screenWidth + widthTop) / 2) + )).calculateRelativeTo(mUnrestricted); + mDisplayCutoutSafe.left = height; + break; + case ROTATION_180: + mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( + new Point((screenWidth - widthTop) / 2, screenHeight), + new Point((screenWidth - widthBottom) / 2, screenHeight - height), + new Point((screenWidth + widthBottom) / 2, screenHeight - height), + new Point((screenWidth + widthTop) / 2, screenHeight) + )).calculateRelativeTo(mUnrestricted); + mDisplayCutoutSafe.bottom = screenHeight - height; + break; + case ROTATION_270: + mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( + new Point(screenHeight, (screenWidth - widthTop) / 2), + new Point(screenHeight - height, (screenWidth - widthBottom) / 2), + new Point(screenHeight - height, (screenWidth + widthBottom) / 2), + new Point(screenHeight, (screenWidth + widthTop) / 2) + )).calculateRelativeTo(mUnrestricted); + mDisplayCutoutSafe.right = screenHeight - height; + break; + default: + mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( + new Point((screenWidth - widthTop) / 2, 0), + new Point((screenWidth - widthBottom) / 2, height), + new Point((screenWidth + widthBottom) / 2, height), + new Point((screenWidth + widthTop) / 2, 0) + )).calculateRelativeTo(mUnrestricted); + mDisplayCutoutSafe.top = height; + break; + } + } + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); mStable.writeToProto(proto, STABLE_BOUNDS); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 4008811b2dc8..b08eb18a6f13 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -101,8 +101,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1; private static final int SET_USER_ACTIVITY_TIMEOUT = 2; - WindowManagerService mService; - private boolean mWallpaperForceHidingChanged = false; private Object mLastWindowFreezeSource = null; private Session mHoldScreen = null; @@ -160,7 +158,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { }; RootWindowContainer(WindowManagerService service) { - mService = service; + super(service); mHandler = new MyHandler(service.mH.getLooper()); mWallpaperController = new WallpaperController(mService); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 8aa129a45373..6ea8a4790630 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -59,7 +59,6 @@ class Task extends WindowContainer<AppWindowToken> { final int mTaskId; final int mUserId; private boolean mDeferRemoval = false; - final WindowManagerService mService; final Rect mPreparedFrozenBounds = new Rect(); final Configuration mPreparedFrozenMergedConfig = new Configuration(); @@ -102,10 +101,10 @@ class Task extends WindowContainer<AppWindowToken> { Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription, TaskWindowContainerController controller) { + super(service); mTaskId = taskId; mStack = stack; mUserId = userId; - mService = service; mResizeMode = resizeMode; mSupportsPictureInPicture = supportsPictureInPicture; setController(controller); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 94fbd0e1f0d9..259f8df15e31 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -197,7 +197,7 @@ class TaskSnapshotSurface implements StartingSurface { } try { final int res = session.addToDisplay(window, window.mSeq, layoutParams, - View.VISIBLE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect, + View.GONE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect, tmpRect, tmpCutout, null); if (res < 0) { Slog.w(TAG, "Failed to add snapshot starting window res=" + res); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 4a3a3fc960a5..832d3957ef1b 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -75,9 +75,6 @@ public class TaskStack extends WindowContainer<Task> implements /** Unique identifier */ final int mStackId; - /** The service */ - private final WindowManagerService mService; - /** The display this stack sits under. */ // TODO: Track parent marks like this in WindowContainer. private DisplayContent mDisplayContent; @@ -151,7 +148,7 @@ public class TaskStack extends WindowContainer<Task> implements final Rect mTmpDimBoundsRect = new Rect(); TaskStack(WindowManagerService service, int stackId, StackWindowController controller) { - mService = service; + super(service); mStackId = stackId; setController(controller); mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize( diff --git a/services/core/java/com/android/server/wm/TransactionFactory.java b/services/core/java/com/android/server/wm/TransactionFactory.java new file mode 100644 index 000000000000..067f08369710 --- /dev/null +++ b/services/core/java/com/android/server/wm/TransactionFactory.java @@ -0,0 +1,27 @@ +/* + * 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 com.android.server.wm; + +import android.view.SurfaceControl.Transaction; + +/** + * Helper class to inject custom transaction objects into window manager. + */ +interface TransactionFactory { + Transaction make(); +}; + diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index c371e8832e5a..d6329bfab5a1 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -72,18 +72,25 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< WindowContainerController mController; protected SurfaceControl mSurfaceControl; + private int mLastLayer = 0; + private SurfaceControl mLastRelativeToLayer = null; /** * Applied as part of the animation pass in "prepareSurfaces". */ - private Transaction mPendingTransaction = new Transaction(); + private final Transaction mPendingTransaction; + protected final WindowManagerService mService; + + WindowContainer(WindowManagerService service) { + mService = service; + mPendingTransaction = service.mTransactionFactory.make(); + } @Override final protected WindowContainer getParent() { return mParent; } - @Override protected int getChildCount() { return mChildren.size(); @@ -756,34 +763,46 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } void assignLayer(Transaction t, int layer) { - if (mSurfaceControl != null) { + final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null; + if (mSurfaceControl != null && changed) { t.setLayer(mSurfaceControl, layer); + mLastLayer = layer; + mLastRelativeToLayer = null; + } + } + + void assignRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) { + final boolean changed = layer != mLastLayer || mLastRelativeToLayer != relativeTo; + if (mSurfaceControl != null && changed) { + t.setRelativeLayer(mSurfaceControl, relativeTo, layer); + mLastLayer = layer; + mLastRelativeToLayer = relativeTo; } } void assignChildLayers(Transaction t) { int layer = 0; - boolean boosting = false; // We use two passes as a way to promote children which // need Z-boosting to the end of the list. - for (int i = 0; i < 2; i++ ) { - for (int j = 0; j < mChildren.size(); ++j) { - final WindowContainer wc = mChildren.get(j); - if (wc.needsZBoost() && !boosting) { - continue; - } - wc.assignLayer(t, layer); - wc.assignChildLayers(t); - - layer++; + for (int j = 0; j < mChildren.size(); ++j) { + final WindowContainer wc = mChildren.get(j); + wc.assignChildLayers(t); + if (!wc.needsZBoost()) { + wc.assignLayer(t, layer++); + } + } + for (int j = 0; j < mChildren.size(); ++j) { + final WindowContainer wc = mChildren.get(j); + if (wc.needsZBoost()) { + wc.assignLayer(t, layer++); } - boosting = true; } } void assignChildLayers() { assignChildLayers(getPendingTransaction()); + scheduleAnimation(); } boolean needsZBoost() { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3ad4df7a7cee..e5982243f94f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -215,6 +215,7 @@ import android.view.MotionEvent; import android.view.PointerIcon; import android.view.Surface; import android.view.SurfaceControl; +import android.view.SurfaceControl.Builder; import android.view.SurfaceSession; import android.view.View; import android.view.WindowContentFrameStats; @@ -806,12 +807,8 @@ public class WindowManagerService extends IWindowManager.Stub static WindowManagerThreadPriorityBooster sThreadPriorityBooster = new WindowManagerThreadPriorityBooster(); - class DefaultSurfaceBuilderFactory implements SurfaceBuilderFactory { - public SurfaceControl.Builder make(SurfaceSession s) { - return new SurfaceControl.Builder(s); - } - }; - SurfaceBuilderFactory mSurfaceBuilderFactory = new DefaultSurfaceBuilderFactory(); + SurfaceBuilderFactory mSurfaceBuilderFactory = SurfaceControl.Builder::new; + TransactionFactory mTransactionFactory = SurfaceControl.Transaction::new; static void boostPriorityForLockedSection() { sThreadPriorityBooster.boost(); @@ -1502,7 +1499,7 @@ public class WindowManagerService extends IWindowManager.Stub // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. - displayContent.assignWindowLayers(false /* setLayoutNeeded */); + win.getParent().assignChildLayers(); if (focusChanged) { mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/); @@ -1972,6 +1969,13 @@ public class WindowManagerService extends IWindowManager.Stub + " newVis=" + viewVisibility, stack); } + win.setDisplayLayoutNeeded(); + win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; + + // We may be deferring layout passes at the moment, but since the client is interested + // in the new out values right now we need to force a layout. + mWindowPlacerLocked.performSurfacePlacement(true /* force */); + // We should only relayout if the view is visible, it is a starting window, or the // associated appToken is not hidden. final boolean shouldRelayout = viewVisibility == View.VISIBLE && @@ -1981,15 +1985,6 @@ public class WindowManagerService extends IWindowManager.Stub if (shouldRelayout) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); - // We are about to create a surface, but we didn't run a layout yet. So better run - // a layout now that we already know the right size, as a resize call will make the - // surface transaction blocking until next vsync and slow us down. - // TODO: Ideally we'd create the surface after running layout a bit further down, - // but moving this seems to be too risky at this point in the release. - if (win.mLayoutSeq == -1) { - win.setDisplayLayoutNeeded(); - mWindowPlacerLocked.performSurfacePlacement(true); - } result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility); try { @@ -2091,16 +2086,11 @@ public class WindowManagerService extends IWindowManager.Stub mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken); } - win.setDisplayLayoutNeeded(); - win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientationFromAppTokens"); configChanged = updateOrientationFromAppTokensLocked(false, displayId); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - // We may be deferring layout passes at the moment, but since the client is interested - // in the new out values right now we need to force a layout. - mWindowPlacerLocked.performSurfacePlacement(true /* force */); if (toBeDisplayed && win.mIsWallpaper) { DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo(); dc.mWallpaperController.updateWallpaperOffset( diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index d23a6c73d853..e38605d33f26 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -186,7 +186,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // to capture touch events in that area. static final int RESIZE_HANDLE_WIDTH_IN_DP = 30; - final WindowManagerService mService; final WindowManagerPolicy mPolicy; final Context mContext; final Session mSession; @@ -627,7 +626,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a, int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) { - mService = service; + super(service); mSession = s; mClient = c; mAppOp = appOp; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index a3d4b71a68af..5bcf59cf1711 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -46,9 +46,6 @@ import java.io.PrintWriter; class WindowToken extends WindowContainer<WindowState> { private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM; - // The window manager! - protected final WindowManagerService mService; - // The actual token. final IBinder token; @@ -107,7 +104,7 @@ class WindowToken extends WindowContainer<WindowState> { WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens) { - mService = service; + super(service); token = _token; windowType = type; mPersistOnEmpty = persistOnEmpty; diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index beffceea7bc7..e12a8da805f6 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -43,11 +43,13 @@ public class PackageManagerServiceTest extends AndroidTestCase { class PackageSenderImpl implements PackageSender { public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, final int flags, final String targetPkg, - final IIntentReceiver finishedReceiver, final int[] userIds) { + final IIntentReceiver finishedReceiver, final int[] userIds, + int[] instantUserIds) { } public void sendPackageAddedForNewUsers(String packageName, - boolean sendBootComplete, boolean includeStopped, int appId, int... userIds) { + boolean sendBootComplete, boolean includeStopped, int appId, + int[] userIds, int[] instantUserIds) { } } diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java index 0941d4fd4fc9..9a6da0e791b5 100644 --- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java +++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java @@ -16,7 +16,12 @@ package com.android.server.policy; +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.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; @@ -29,6 +34,7 @@ import android.graphics.PixelFormat; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.view.Surface; import android.view.WindowManager; import org.junit.Before; @@ -105,4 +111,123 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase { assertEquals(0, mAppWindow.attrs.systemUiVisibility); assertEquals(0, mAppWindow.attrs.subtreeSystemUiVisibility); } + + @Test + public void layoutWindowLw_withDisplayCutout() { + addDisplayCutout(); + + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0); + assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreen() { + addDisplayCutout(); + + mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0); + assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() { + addDisplayCutout(); + + mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + mAppWindow.attrs.flags2 |= FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA; + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0); + assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0); + } + + + @Test + public void layoutWindowLw_withDisplayCutout_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetBy(mAppWindow.parentFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.contentFrame, + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_seascape() { + addDisplayCutout(); + setRotation(ROTATION_270); + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetBy(mAppWindow.parentFrame, 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetBy(mAppWindow.stableFrame, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); + assertInsetBy(mAppWindow.contentFrame, + NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + + mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetBy(mAppWindow.parentFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.contentFrame, + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + + mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + mAppWindow.attrs.flags2 |= FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA; + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetBy(mAppWindow.parentFrame, 0, 0, 0, 0); + assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.contentFrame, + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0); + } + }
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java index 9c5570725baa..e7e9abad5bbe 100644 --- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java +++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java @@ -17,6 +17,9 @@ package com.android.server.policy; import static android.view.Surface.ROTATION_0; +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.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; @@ -54,6 +57,7 @@ public class PhoneWindowManagerTestBase { static final int STATUS_BAR_HEIGHT = 10; static final int NAV_BAR_HEIGHT = 15; + static final int DISPLAY_CUTOUT_HEIGHT = 8; TestablePhoneWindowManager mPolicy; TestContextWrapper mContext; @@ -76,10 +80,17 @@ public class PhoneWindowManagerTestBase { mPolicy = TestablePhoneWindowManager.create(mContext); + setRotation(ROTATION_0); + } + + public void setRotation(int rotation) { DisplayInfo info = new DisplayInfo(); - info.logicalWidth = DISPLAY_WIDTH; - info.logicalHeight = DISPLAY_HEIGHT; - info.rotation = ROTATION_0; + + final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270; + info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + info.rotation = rotation; + mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info); } @@ -95,7 +106,7 @@ public class PhoneWindowManagerTestBase { public void addNavigationBar() { mNavigationBar = new FakeWindowState(); - mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, STATUS_BAR_HEIGHT, + mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, NAV_BAR_HEIGHT, TYPE_NAVIGATION_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT); mNavigationBar.attrs.gravity = Gravity.BOTTOM; @@ -104,11 +115,16 @@ public class PhoneWindowManagerTestBase { mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT; } + public void addDisplayCutout() { + mPolicy.mEmulateDisplayCutout = true; + } + /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */ public void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop, int expectedInsetRight, int expectedInsetBottom) { assertEquals(new Rect(expectedInsetLeft, expectedInsetTop, - DISPLAY_WIDTH - expectedInsetRight, DISPLAY_HEIGHT - expectedInsetBottom), actual); + mFrames.mDisplayWidth - expectedInsetRight, + mFrames.mDisplayHeight - expectedInsetBottom), actual); } /** @@ -181,6 +197,11 @@ public class PhoneWindowManagerTestBase { policy[0].mAccessibilityManager = new AccessibilityManager(context, mock(IAccessibilityManager.class), UserHandle.USER_CURRENT); policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class); + policy[0].mNavigationBarCanMove = true; + policy[0].mPortraitRotation = ROTATION_0; + policy[0].mLandscapeRotation = ROTATION_90; + policy[0].mUpsideDownRotation = ROTATION_180; + policy[0].mSeascapeRotation = ROTATION_270; policy[0].onConfigurationChanged(); }); return policy[0]; diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java index f069d49a244b..4dd51ebd2d06 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java @@ -48,6 +48,10 @@ public class DimmerTests extends WindowTestsBase { final SurfaceControl mControl = mock(SurfaceControl.class); final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class); + TestWindowContainer() { + super(sWm); + } + @Override SurfaceControl getSurfaceControl() { return mControl; @@ -65,6 +69,10 @@ public class DimmerTests extends WindowTestsBase { final SurfaceControl mHostControl = mock(SurfaceControl.class); final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class); + MockSurfaceBuildingContainer() { + super(sWm); + } + class MockSurfaceBuilder extends SurfaceControl.Builder { MockSurfaceBuilder(SurfaceSession ss) { super(ss); @@ -78,6 +86,7 @@ public class DimmerTests extends WindowTestsBase { } } + @Override SurfaceControl.Builder makeChildSurface(WindowContainer child) { return new MockSurfaceBuilder(mSession); diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java index 5da322409f83..ab0a2bd86dd8 100644 --- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java @@ -34,7 +34,7 @@ import static org.junit.Assert.assertTrue; * Test class for {@link StackWindowController}. * * Build/Install/Run: - * bit FrameworksServicesTests:com.android.server.wm.StackWindowControllerTests + * atest FrameworksServicesTests:StackWindowControllerTests */ @SmallTest @Presubmit @@ -61,7 +61,6 @@ public class StackWindowControllerTests extends WindowTestsBase { } @Test - @Ignore("b/65379195") public void testRemoveContainer_deferRemoval() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(mDisplayContent); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java index 8df7568ebcd9..bab2170ad7bb 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java @@ -44,7 +44,7 @@ public class WindowContainerControllerTests extends WindowTestsBase { @Test public void testCreation() throws Exception { final WindowContainerController controller = new WindowContainerController(null, sWm); - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); container.setController(controller); assertEquals(controller, container.getController()); @@ -54,7 +54,7 @@ public class WindowContainerControllerTests extends WindowTestsBase { @Test public void testSetContainer() throws Exception { final WindowContainerController controller = new WindowContainerController(null, sWm); - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); controller.setContainer(container); assertEquals(controller.mContainer, container); @@ -62,7 +62,7 @@ public class WindowContainerControllerTests extends WindowTestsBase { // Assert we can't change the container to another one once set boolean gotException = false; try { - controller.setContainer(new WindowContainer()); + controller.setContainer(new WindowContainer(sWm)); } catch (IllegalArgumentException e) { gotException = true; } @@ -76,7 +76,7 @@ public class WindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer() throws Exception { final WindowContainerController controller = new WindowContainerController(null, sWm); - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); controller.setContainer(container); assertEquals(controller.mContainer, container); @@ -88,7 +88,7 @@ public class WindowContainerControllerTests extends WindowTestsBase { @Test public void testOnOverrideConfigurationChanged() throws Exception { final WindowContainerController controller = new WindowContainerController(null, sWm); - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); controller.setContainer(container); assertEquals(controller.mContainer, container); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java index 16b6ca684c78..5cb94678a58c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java @@ -193,7 +193,7 @@ public class WindowContainerTests extends WindowTestsBase { @Test public void testRemoveImmediately_WithController() throws Exception { - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); final WindowContainerController controller = new WindowContainerController(null, sWm); container.setController(controller); @@ -208,7 +208,7 @@ public class WindowContainerTests extends WindowTestsBase { @Test public void testSetController() throws Exception { final WindowContainerController controller = new WindowContainerController(null, sWm); - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); container.setController(controller); assertEquals(controller, container.getController()); @@ -587,6 +587,7 @@ public class WindowContainerTests extends WindowTestsBase { TestWindowContainer(int layer, boolean isAnimating, boolean isVisible, Integer orientation) { + super(sWm); mLayer = layer; mIsAnimating = isAnimating; mIsVisible = isVisible; diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java index e5cbdba7b507..6468763440a5 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java @@ -16,23 +16,6 @@ package com.android.server.wm; -import java.util.HashMap; -import java.util.LinkedList; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.util.Log; - -import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; - import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -42,10 +25,23 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; + +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.SurfaceControl; +import android.view.SurfaceSession; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.LinkedList; /** * Tests for the {@link WindowLayersController} class. @@ -79,7 +75,7 @@ public class ZOrderingTests extends WindowTestsBase { } int getLayer(SurfaceControl sc) { - return mLayersForControl.get(sc); + return mLayersForControl.getOrDefault(sc, 0); } SurfaceControl getRelativeLayer(SurfaceControl sc) { @@ -125,6 +121,7 @@ public class ZOrderingTests extends WindowTestsBase { // would miss construction of the top-level layers. mTransaction = new LayerRecordingTransaction(); sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory(); + sWm.mTransactionFactory = () -> mTransaction; } @After diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java index 498d517c104b..ffe721993a37 100644 --- a/tests/testables/src/android/testing/TestableContext.java +++ b/tests/testables/src/android/testing/TestableContext.java @@ -25,6 +25,7 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; @@ -69,6 +70,7 @@ public class TestableContext extends ContextWrapper implements TestRule { private LeakCheck.Tracker mService; private LeakCheck.Tracker mComponent; private TestableResources mTestableResources; + private TestablePermissions mTestablePermissions; public TestableContext(Context base) { this(base, null); @@ -302,6 +304,159 @@ public class TestableContext extends ContextWrapper implements TestRule { super.unregisterComponentCallbacks(callback); } + public TestablePermissions getTestablePermissions() { + if (mTestablePermissions == null) { + mTestablePermissions = new TestablePermissions(); + } + return mTestablePermissions; + } + + @Override + public int checkCallingOrSelfPermission(String permission) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkCallingOrSelfPermission(permission); + } + + @Override + public int checkCallingPermission(String permission) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkCallingPermission(permission); + } + + @Override + public int checkPermission(String permission, int pid, int uid) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkPermission(permission, pid, uid); + } + + @Override + public int checkPermission(String permission, int pid, int uid, IBinder callerToken) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkPermission(permission, pid, uid, callerToken); + } + + @Override + public int checkSelfPermission(String permission) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkSelfPermission(permission); + } + + @Override + public void enforceCallingOrSelfPermission(String permission, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + mTestablePermissions.enforce(permission); + } else { + super.enforceCallingOrSelfPermission(permission, message); + } + } + + @Override + public void enforceCallingPermission(String permission, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + mTestablePermissions.enforce(permission); + } else { + super.enforceCallingPermission(permission, message); + } + } + + @Override + public void enforcePermission(String permission, int pid, int uid, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + mTestablePermissions.enforce(permission); + } else { + super.enforcePermission(permission, pid, uid, message); + } + } + + @Override + public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkCallingOrSelfUriPermission(uri, modeFlags); + } + + @Override + public int checkCallingUriPermission(Uri uri, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkCallingUriPermission(uri, modeFlags); + } + + @Override + public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceCallingOrSelfUriPermission(uri, modeFlags, message); + } + } + + @Override + public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkUriPermission(uri, pid, uid, modeFlags); + } + + @Override + public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkUriPermission(uri, pid, uid, modeFlags, callerToken); + } + + @Override + public int checkUriPermission(Uri uri, String readPermission, String writePermission, int pid, + int uid, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkUriPermission(uri, readPermission, writePermission, pid, uid, modeFlags); + } + + @Override + public void enforceCallingUriPermission(Uri uri, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceCallingUriPermission(uri, modeFlags, message); + } + } + + @Override + public void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceUriPermission(uri, pid, uid, modeFlags, message); + } + } + + @Override + public void enforceUriPermission(Uri uri, String readPermission, String writePermission, + int pid, int uid, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceUriPermission(uri, readPermission, writePermission, pid, uid, modeFlags, + message); + } + } + @Override public Statement apply(Statement base, Description description) { return new TestWatcher() { diff --git a/tests/testables/src/android/testing/TestablePermissions.java b/tests/testables/src/android/testing/TestablePermissions.java new file mode 100644 index 000000000000..4f009e406ca7 --- /dev/null +++ b/tests/testables/src/android/testing/TestablePermissions.java @@ -0,0 +1,80 @@ +/* + * 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.testing; + +import android.content.pm.PackageManager; +import android.net.Uri; +import android.util.ArrayMap; + +/** + * Simple class for simulating basic permission states for tests. + * + * All enforce* and check* calls on TestableContext are considered the same + * and routed through the same check here. If more fine-grained control is + * required, then either a sub-class or spy on TestableContext is recommended. + */ +public class TestablePermissions { + + private final ArrayMap<String, Integer> mPermissions = new ArrayMap<>(); + private final ArrayMap<Uri, Integer> mUris = new ArrayMap<>(); + + /** + * Sets the return value for checkPermission* calls on TestableContext + * for a specific permission value. For all enforcePermission* calls + * they will throw a security exception if value != PERMISSION_GRANTED. + */ + public void setPermission(String permission, int value) { + mPermissions.put(permission, value); + } + + /** + * Sets the return value for checkUriPermission* calls on TestableContext + * for a specific permission value. For all enforceUriPermission* calls + * they will throw a security exception if value != PERMISSION_GRANTED. + */ + public void setPermission(Uri uri, int value) { + // TODO: Support modeFlags + mUris.put(uri, value); + } + + boolean wantsCall(String permission) { + return mPermissions.containsKey(permission); + } + + boolean wantsCall(Uri uri) { + return mUris.containsKey(uri); + } + + int check(String permission) { + return mPermissions.get(permission); + } + + int check(Uri uri, int modeFlags) { + // TODO: Support modeFlags + return mUris.get(uri); + } + + public void enforce(String permission) { + if (check(permission) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(); + } + } + + public void enforce(Uri uri, int modeFlags) { + if (check(uri, modeFlags) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(); + } + } +} diff --git a/tests/testables/tests/src/android/testing/TestablePermissionsTest.java b/tests/testables/tests/src/android/testing/TestablePermissionsTest.java new file mode 100644 index 000000000000..c56146e19a40 --- /dev/null +++ b/tests/testables/tests/src/android/testing/TestablePermissionsTest.java @@ -0,0 +1,90 @@ +/* + * 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.testing; + +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import static org.junit.Assert.assertEquals; + +import android.Manifest.permission; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.testing.TestableLooper.RunWithLooper; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class TestablePermissionsTest { + + private static final Uri URI_1 = Uri.parse("content://my.authority/path1"); + private static final Uri URI_2 = Uri.parse("content://my.authority/path2"); + + @Rule + public TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext()); + + @Test + public void testCheck() { + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS, + PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS_FULL, + PERMISSION_DENIED); + assertEquals(PERMISSION_GRANTED, + mContext.checkPermission(permission.INTERACT_ACROSS_USERS, 0, 0)); + assertEquals(PERMISSION_DENIED, + mContext.checkPermission(permission.INTERACT_ACROSS_USERS_FULL, 0, 0)); + } + + @Test + public void testCheckUri() { + mContext.getTestablePermissions().setPermission(URI_1, PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission(URI_2, PERMISSION_DENIED); + + assertEquals(PERMISSION_GRANTED, mContext.checkUriPermission(URI_1, 0, 0, 0)); + assertEquals(PERMISSION_DENIED, mContext.checkUriPermission(URI_2, 0, 0, 0)); + } + + @Test + public void testEnforceNoException() { + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS, + PERMISSION_GRANTED); + mContext.enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS, ""); + } + + @Test(expected = SecurityException.class) + public void testEnforceWithException() { + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS, + PERMISSION_DENIED); + mContext.enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS, ""); + } + + @Test + public void testEnforceUriNoException() { + mContext.getTestablePermissions().setPermission(URI_1, PERMISSION_GRANTED); + mContext.enforceUriPermission(URI_1, 0, 0, 0, ""); + } + + @Test(expected = SecurityException.class) + public void testEnforceUriWithException() { + mContext.getTestablePermissions().setPermission(URI_1, PERMISSION_DENIED); + mContext.enforceUriPermission(URI_1, 0, 0, 0, ""); + } + +}
\ No newline at end of file |