summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp8
-rw-r--r--core/java/android/app/ActivityThread.java4
-rw-r--r--core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java33
-rw-r--r--core/java/android/app/servertransaction/ActivityResultItem.java31
-rw-r--r--core/java/android/app/servertransaction/BaseClientRequest.java2
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java42
-rw-r--r--core/java/android/app/servertransaction/ConfigurationChangeItem.java33
-rw-r--r--core/java/android/app/servertransaction/DestroyActivityItem.java33
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java142
-rw-r--r--core/java/android/app/servertransaction/MoveToDisplayItem.java37
-rw-r--r--core/java/android/app/servertransaction/MultiWindowModeChangeItem.java39
-rw-r--r--core/java/android/app/servertransaction/NewIntentItem.java36
-rw-r--r--core/java/android/app/servertransaction/ObjectPool.java73
-rw-r--r--core/java/android/app/servertransaction/ObjectPoolItem.java29
-rw-r--r--core/java/android/app/servertransaction/PauseActivityItem.java64
-rw-r--r--core/java/android/app/servertransaction/PipModeChangeItem.java38
-rw-r--r--core/java/android/app/servertransaction/ResumeActivityItem.java57
-rw-r--r--core/java/android/app/servertransaction/StopActivityItem.java33
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java4
-rw-r--r--core/java/android/app/servertransaction/WindowVisibilityItem.java28
-rw-r--r--core/java/android/provider/Settings.java10
-rw-r--r--core/java/android/view/WindowManager.java29
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java2
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java287
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TestUtils.java74
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java8
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java100
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/res/values/config.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java140
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java12
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java16
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java21
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java1
-rw-r--r--services/core/java/com/android/server/am/ClientLifecycleManager.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java221
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java77
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java87
-rw-r--r--services/core/java/com/android/server/wm/DisplayFrames.java58
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java3
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java5
-rw-r--r--services/core/java/com/android/server/wm/TransactionFactory.java27
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java49
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java32
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java125
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/DimmerTests.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java35
-rw-r--r--tests/testables/src/android/testing/TestableContext.java155
-rw-r--r--tests/testables/src/android/testing/TestablePermissions.java80
-rw-r--r--tests/testables/tests/src/android/testing/TestablePermissionsTest.java90
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