diff --git a/api/current.txt b/api/current.txt
index 8f505ccdadce6..f98582f7a597e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2684,6 +2684,7 @@ package android.app {
method public final boolean isChild();
method public boolean isDestroyed();
method public boolean isFinishing();
+ method public boolean isImmersive();
method public boolean isTaskRoot();
method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public boolean moveTaskToBack(boolean);
@@ -2771,6 +2772,7 @@ package android.app {
method public final void setFeatureDrawableResource(int, int);
method public final void setFeatureDrawableUri(int, android.net.Uri);
method public void setFinishOnTouchOutside(boolean);
+ method public void setImmersive(boolean);
method public void setIntent(android.content.Intent);
method public final void setProgress(int);
method public final void setProgressBarIndeterminate(boolean);
@@ -6331,6 +6333,7 @@ package android.content.pm {
field public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 256; // 0x100
field public static final int FLAG_FINISH_ON_TASK_LAUNCH = 2; // 0x2
field public static final int FLAG_HARDWARE_ACCELERATED = 512; // 0x200
+ field public static final int FLAG_IMMERSIVE = 2048; // 0x800
field public static final int FLAG_MULTIPROCESS = 1; // 0x1
field public static final int FLAG_NO_HISTORY = 128; // 0x80
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f6b9a8e096ed3..cbeffc1b8008f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4818,8 +4818,8 @@ public class Activity extends ContextThemeWrapper
* android:immersive but may be changed at runtime by
* {@link #setImmersive}.
*
+ * @see #setImmersive(boolean)
* @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
- * @hide
*/
public boolean isImmersive() {
try {
@@ -4831,7 +4831,7 @@ public class Activity extends ContextThemeWrapper
/**
* Adjust the current immersive mode setting.
- *
+ *
* Note that changing this value will have no effect on the activity's
* {@link android.content.pm.ActivityInfo} structure; that is, if
* android:immersive is set to true
@@ -4840,9 +4840,8 @@ public class Activity extends ContextThemeWrapper
* always have its {@link android.content.pm.ActivityInfo#FLAG_IMMERSIVE
* FLAG_IMMERSIVE} bit set.
*
- * @see #isImmersive
+ * @see #isImmersive()
* @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
- * @hide
*/
public void setImmersive(boolean i) {
try {
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e2ca1ddea3762..8f3b62d8f21cf 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -161,7 +161,6 @@ public class ActivityInfo extends ComponentInfo
*/
public static final int FLAG_SHOW_ON_LOCK_SCREEN = 0x0400;
/**
- * @hide
* Bit in {@link #flags} corresponding to an immersive activity
* that wishes not to be interrupted by notifications.
* Applications that hide the system notification bar with
@@ -174,7 +173,14 @@ public class ActivityInfo extends ComponentInfo
* {@link #FLAG_IMMERSIVE} set, however, will not be interrupted; the
* notification may be shown in some other way (such as a small floating
* "toast" window).
- * {@see android.app.Notification#FLAG_HIGH_PRIORITY}
+ *
+ * Note that this flag will always reflect the Activity's
+ * android:immersive manifest definition, even if the Activity's
+ * immersive state is changed at runtime via
+ * {@link android.app.Activity#setImmersive(boolean)}.
+ *
+ * @see android.app.Notification#FLAG_HIGH_PRIORITY
+ * @see android.app.Activity#setImmersive(boolean)
*/
public static final int FLAG_IMMERSIVE = 0x0800;
/**
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 60b208dfaa81f..8728976483de9 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -115,6 +115,7 @@ import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UpdateLock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.Time;
@@ -191,6 +192,7 @@ public final class ActivityManagerService extends ActivityManagerNative
static final boolean DEBUG_POWER = localLOGV || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
static final boolean DEBUG_MU = localLOGV || false;
+ static final boolean DEBUG_IMMERSIVE = localLOGV || false;
static final boolean VALIDATE_TOKENS = false;
static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -826,6 +828,12 @@ public final class ActivityManagerService extends ActivityManagerNative
long mLastWriteTime = 0;
+ /**
+ * Used to retain an update lock when the foreground activity is in
+ * immersive mode.
+ */
+ final UpdateLock mUpdateLock = new UpdateLock("immersive");
+
/**
* Set to true after the system has finished booting.
*/
@@ -895,6 +903,7 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int REPORT_USER_SWITCH_MSG = 34;
static final int CONTINUE_USER_SWITCH_MSG = 35;
static final int USER_SWITCH_TIMEOUT_MSG = 36;
+ static final int IMMERSIVE_MODE_LOCK_MSG = 37;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1356,6 +1365,21 @@ public final class ActivityManagerService extends ActivityManagerNative
timeoutUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
break;
}
+ case IMMERSIVE_MODE_LOCK_MSG: {
+ final boolean nextState = (msg.arg1 != 0);
+ if (mUpdateLock.isHeld() != nextState) {
+ if (DEBUG_IMMERSIVE) {
+ final ActivityRecord r = (ActivityRecord) msg.obj;
+ Slog.d(TAG, "Applying new update lock state '" + nextState + "' for " + r);
+ }
+ if (nextState) {
+ mUpdateLock.acquire();
+ } else {
+ mUpdateLock.release();
+ }
+ }
+ break;
+ }
}
}
};
@@ -1824,9 +1848,20 @@ public final class ActivityManagerService extends ActivityManagerNative
if (r != null) {
mWindowManager.setFocusedApp(r.appToken, true);
}
+ applyUpdateLockStateLocked(r);
}
}
+ final void applyUpdateLockStateLocked(ActivityRecord r) {
+ // Modifications to the UpdateLock state are done on our handler, outside
+ // the activity manager's locks. The new state is determined based on the
+ // state *now* of the relevant activity record. The object is passed to
+ // the handler solely for logging detail, not to be consulted/modified.
+ final boolean nextState = r != null && r.immersive;
+ mHandler.sendMessage(
+ mHandler.obtainMessage(IMMERSIVE_MODE_LOCK_MSG, (nextState) ? 1 : 0, 0, r));
+ }
+
private final void updateLruProcessInternalLocked(ProcessRecord app, int bestPos) {
// put it on the LRU to keep track of when it should be exited.
int lrui = mLruProcesses.indexOf(app);
@@ -7423,11 +7458,19 @@ public final class ActivityManagerService extends ActivityManagerNative
public void setImmersive(IBinder token, boolean immersive) {
synchronized(this) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ final ActivityRecord r = mMainStack.isInStackLocked(token);
if (r == null) {
throw new IllegalArgumentException();
}
r.immersive = immersive;
+
+ // update associated state if we're frontmost
+ if (r == mFocusedActivity) {
+ if (DEBUG_IMMERSIVE) {
+ Slog.d(TAG, "Frontmost changed immersion: "+ r);
+ }
+ applyUpdateLockStateLocked(r);
+ }
}
}