Merge "Implement screen recording detection API internals" into main
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 36b74e3..7903050 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -69,12 +69,13 @@
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.window.AddToSurfaceSyncGroupResult;
+import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
-import android.window.ScreenCapture;
-import android.window.WindowContextInfo;
import android.window.ITrustedPresentationListener;
+import android.window.ScreenCapture;
import android.window.TrustedPresentationThresholds;
+import android.window.WindowContextInfo;
/**
* System private interface to the window manager.
@@ -1083,4 +1084,8 @@
void unregisterTrustedPresentationListener(in ITrustedPresentationListener listener, int id);
+
+ boolean registerScreenRecordingCallback(IScreenRecordingCallback callback);
+
+ void unregisterScreenRecordingCallback(IScreenRecordingCallback callback);
}
diff --git a/core/java/android/window/IScreenRecordingCallback.aidl b/core/java/android/window/IScreenRecordingCallback.aidl
new file mode 100644
index 0000000..560ee75
--- /dev/null
+++ b/core/java/android/window/IScreenRecordingCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 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.window;
+
+/**
+ * @hide
+ */
+oneway interface IScreenRecordingCallback {
+ void onScreenRecordingStateChanged(boolean visibleInScreenRecording);
+}
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 3c3c846..751c1a8 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -80,3 +80,11 @@
is_fixed_read_only: true
bug: "278757236"
}
+
+flag {
+ namespace: "window_surfaces"
+ name: "screen_recording_callbacks"
+ description: "Enable screen recording callbacks public API"
+ is_fixed_read_only: true
+ bug: "304574518"
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 917a300..3a778c3 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -535,6 +535,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
+ "-1583619037": {
+ "message": "Failed to register MediaProjectionWatcherCallback",
+ "level": "ERROR",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/ScreenRecordingCallbackController.java"
+ },
"-1582845629": {
"message": "Starting animation on %s",
"level": "VERBOSE",
@@ -2983,12 +2989,6 @@
"group": "WM_DEBUG_SCREEN_ON",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "466506262": {
- "message": "Clear freezing of %s: visible=%b freezing=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"485170982": {
"message": "Not finishing noHistory %s on stop because we're just sleeping",
"level": "DEBUG",
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 8ce1b6d..3d927d3 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -121,7 +121,7 @@
@EnforcePermission("MANAGE_MEDIA_PROJECTION")
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
- void addCallback(IMediaProjectionWatcherCallback callback);
+ MediaProjectionInfo addCallback(IMediaProjectionWatcherCallback callback);
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
diff --git a/media/java/android/media/projection/MediaProjectionInfo.java b/media/java/android/media/projection/MediaProjectionInfo.java
index ff60856..c820392 100644
--- a/media/java/android/media/projection/MediaProjectionInfo.java
+++ b/media/java/android/media/projection/MediaProjectionInfo.java
@@ -16,6 +16,7 @@
package android.media.projection;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -26,15 +27,18 @@
public final class MediaProjectionInfo implements Parcelable {
private final String mPackageName;
private final UserHandle mUserHandle;
+ private final IBinder mLaunchCookie;
- public MediaProjectionInfo(String packageName, UserHandle handle) {
+ public MediaProjectionInfo(String packageName, UserHandle handle, IBinder launchCookie) {
mPackageName = packageName;
mUserHandle = handle;
+ mLaunchCookie = launchCookie;
}
public MediaProjectionInfo(Parcel in) {
mPackageName = in.readString();
mUserHandle = UserHandle.readFromParcel(in);
+ mLaunchCookie = in.readStrongBinder();
}
public String getPackageName() {
@@ -45,12 +49,16 @@
return mUserHandle;
}
+ public IBinder getLaunchCookie() {
+ return mLaunchCookie;
+ }
+
@Override
public boolean equals(Object o) {
- if (o instanceof MediaProjectionInfo) {
- final MediaProjectionInfo other = (MediaProjectionInfo) o;
+ if (o instanceof MediaProjectionInfo other) {
return Objects.equals(other.mPackageName, mPackageName)
- && Objects.equals(other.mUserHandle, mUserHandle);
+ && Objects.equals(other.mUserHandle, mUserHandle)
+ && Objects.equals(other.mLaunchCookie, mLaunchCookie);
}
return false;
}
@@ -64,7 +72,8 @@
public String toString() {
return "MediaProjectionInfo{mPackageName="
+ mPackageName + ", mUserHandle="
- + mUserHandle + "}";
+ + mUserHandle + ", mLaunchCookie"
+ + mLaunchCookie + "}";
}
@Override
@@ -76,6 +85,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(mPackageName);
UserHandle.writeToParcel(mUserHandle, out);
+ out.writeStrongBinder(mLaunchCookie);
}
public static final @android.annotation.NonNull Parcelable.Creator<MediaProjectionInfo> CREATOR =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt
index 45f0a8c..44c411f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt
@@ -66,6 +66,11 @@
private const val DEFAULT_PACKAGE_NAME = "com.media.projection.test"
private val DEFAULT_USER_HANDLE = UserHandle.getUserHandleForUid(UserHandle.myUserId())
- private val DEFAULT_INFO = MediaProjectionInfo(DEFAULT_PACKAGE_NAME, DEFAULT_USER_HANDLE)
+ private val DEFAULT_INFO =
+ MediaProjectionInfo(
+ DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_HANDLE,
+ /* launchCookie = */ null
+ )
}
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index f6571d9..550aed5 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -304,7 +304,7 @@
}
@VisibleForTesting
- void addCallback(final IMediaProjectionWatcherCallback callback) {
+ MediaProjectionInfo addCallback(final IMediaProjectionWatcherCallback callback) {
IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
@@ -314,6 +314,7 @@
synchronized (mLock) {
mCallbackDelegate.add(callback);
linkDeathRecipientLocked(callback, deathRecipient);
+ return mProjectionGrant != null ? mProjectionGrant.getProjectionInfo() : null;
}
}
@@ -786,11 +787,11 @@
@Override //Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
- public void addCallback(final IMediaProjectionWatcherCallback callback) {
+ public MediaProjectionInfo addCallback(final IMediaProjectionWatcherCallback callback) {
addCallback_enforcePermission();
final long token = Binder.clearCallingIdentity();
try {
- MediaProjectionManagerService.this.addCallback(callback);
+ return MediaProjectionManagerService.this.addCallback(callback);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1244,7 +1245,7 @@
}
public MediaProjectionInfo getProjectionInfo() {
- return new MediaProjectionInfo(packageName, userHandle);
+ return new MediaProjectionInfo(packageName, userHandle, mLaunchCookie);
}
boolean requiresForegroundService() {
diff --git a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
new file mode 100644
index 0000000..5f488b7
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.Context.MEDIA_PROJECTION_SERVICE;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
+
+import android.media.projection.IMediaProjectionManager;
+import android.media.projection.IMediaProjectionWatcherCallback;
+import android.media.projection.MediaProjectionInfo;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.view.ContentRecordingSession;
+import android.window.IScreenRecordingCallback;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.Set;
+
+public class ScreenRecordingCallbackController {
+
+ private final class Callback implements IBinder.DeathRecipient {
+
+ IScreenRecordingCallback mCallback;
+ int mUid;
+
+ Callback(IScreenRecordingCallback callback, int uid) {
+ this.mCallback = callback;
+ this.mUid = uid;
+ }
+
+ public void binderDied() {
+ unregister(mCallback);
+ }
+ }
+
+ @GuardedBy("WindowManagerService.mGlobalLock")
+ private final Map<IBinder, Callback> mCallbacks = new ArrayMap<>();
+
+ @GuardedBy("WindowManagerService.mGlobalLock")
+ private final Map<Integer /*UID*/, Boolean> mLastInvokedStateByUid = new ArrayMap<>();
+
+ private final WindowManagerService mWms;
+
+ @GuardedBy("WindowManagerService.mGlobalLock")
+ private WindowContainer<WindowContainer> mRecordedWC;
+
+ private boolean mWatcherCallbackRegistered = false;
+
+ private final class MediaProjectionWatcherCallback extends
+ IMediaProjectionWatcherCallback.Stub {
+ @Override
+ public void onStart(MediaProjectionInfo mediaProjectionInfo) {
+ onScreenRecordingStart(mediaProjectionInfo);
+ }
+
+ @Override
+ public void onStop(MediaProjectionInfo mediaProjectionInfo) {
+ onScreenRecordingStop();
+ }
+
+ @Override
+ public void onRecordingSessionSet(MediaProjectionInfo mediaProjectionInfo,
+ ContentRecordingSession contentRecordingSession) {
+ }
+ }
+
+ ScreenRecordingCallbackController(WindowManagerService wms) {
+ mWms = wms;
+ }
+
+ @GuardedBy("WindowManagerService.mGlobalLock")
+ private void setRecordedWindowContainer(MediaProjectionInfo mediaProjectionInfo) {
+ if (mediaProjectionInfo.getLaunchCookie() == null) {
+ mRecordedWC = (WindowContainer) mWms.mRoot.getDefaultDisplay();
+ } else {
+ mRecordedWC = mWms.mRoot.getActivity(activity -> activity.mLaunchCookie
+ == mediaProjectionInfo.getLaunchCookie()).getTask();
+ }
+ }
+
+ @GuardedBy("WindowManagerService.mGlobalLock")
+ private void ensureMediaProjectionWatcherCallbackRegistered() {
+ if (mWatcherCallbackRegistered) {
+ return;
+ }
+
+ IBinder binder = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
+ IMediaProjectionManager mediaProjectionManager =
+ IMediaProjectionManager.Stub.asInterface(binder);
+
+ long identityToken = Binder.clearCallingIdentity();
+ MediaProjectionInfo mediaProjectionInfo = null;
+ try {
+ mediaProjectionInfo = mediaProjectionManager.addCallback(
+ new MediaProjectionWatcherCallback());
+ mWatcherCallbackRegistered = true;
+ } catch (RemoteException e) {
+ ProtoLog.e(WM_ERROR, "Failed to register MediaProjectionWatcherCallback");
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+
+ if (mediaProjectionInfo != null) {
+ setRecordedWindowContainer(mediaProjectionInfo);
+ }
+ }
+
+ boolean register(IScreenRecordingCallback callback) {
+ synchronized (mWms.mGlobalLock) {
+ ensureMediaProjectionWatcherCallbackRegistered();
+
+ IBinder binder = callback.asBinder();
+ int uid = Binder.getCallingUid();
+
+ if (mCallbacks.containsKey(binder)) {
+ return mLastInvokedStateByUid.get(uid);
+ }
+
+ Callback callbackInfo = new Callback(callback, uid);
+ try {
+ binder.linkToDeath(callbackInfo, 0);
+ } catch (RemoteException e) {
+ return false;
+ }
+
+ boolean uidInRecording = uidHasRecordedActivity(callbackInfo.mUid);
+ mLastInvokedStateByUid.put(callbackInfo.mUid, uidInRecording);
+ mCallbacks.put(binder, callbackInfo);
+ return uidInRecording;
+ }
+ }
+
+ void unregister(IScreenRecordingCallback callback) {
+ synchronized (mWms.mGlobalLock) {
+ IBinder binder = callback.asBinder();
+ Callback callbackInfo = mCallbacks.remove(binder);
+ binder.unlinkToDeath(callbackInfo, 0);
+
+ boolean uidHasCallback = false;
+ for (Callback cb : mCallbacks.values()) {
+ if (cb.mUid == callbackInfo.mUid) {
+ uidHasCallback = true;
+ break;
+ }
+ }
+ if (!uidHasCallback) {
+ mLastInvokedStateByUid.remove(callbackInfo.mUid);
+ }
+ }
+ }
+
+ private void onScreenRecordingStart(MediaProjectionInfo mediaProjectionInfo) {
+ synchronized (mWms.mGlobalLock) {
+ setRecordedWindowContainer(mediaProjectionInfo);
+ dispatchCallbacks(getRecordedUids(), true /* visibleInScreenRecording*/);
+ }
+ }
+
+ private void onScreenRecordingStop() {
+ synchronized (mWms.mGlobalLock) {
+ dispatchCallbacks(getRecordedUids(), false /*visibleInScreenRecording*/);
+ mRecordedWC = null;
+ }
+ }
+
+ @GuardedBy("WindowManagerService.mGlobalLock")
+ void onProcessActivityVisibilityChanged(int uid, boolean processVisible) {
+ // If recording isn't active or there's no registered callback for the uid, there's nothing
+ // to do on this visibility change.
+ if (mRecordedWC == null || !mLastInvokedStateByUid.containsKey(uid)) {
+ return;
+ }
+
+ // If the callbacks are already in the correct state, avoid making duplicate callbacks for
+ // the same state. This can happen when:
+ // * a process becomes visible but its UID already has a recorded activity from another
+ // process.
+ // * a process becomes invisible but its UID already doesn't have any recorded activities.
+ if (processVisible == mLastInvokedStateByUid.get(uid)) {
+ return;
+ }
+
+ // If the process visibility change doesn't change the visibility of the UID, avoid making
+ // duplicate callbacks for the same state. This can happen when:
+ // * a process becomes visible but the newly visible activity isn't in the recorded window
+ // container.
+ // * a process becomes invisible but there are still activities being recorded for the UID.
+ boolean uidInRecording = uidHasRecordedActivity(uid);
+ if ((processVisible && !uidInRecording) || (!processVisible && uidInRecording)) {
+ return;
+ }
+
+ dispatchCallbacks(Set.of(uid), processVisible);
+ }
+
+ @GuardedBy("WindowManagerService.mGlobalLock")
+ private boolean uidHasRecordedActivity(int uid) {
+ if (mRecordedWC == null) {
+ return false;
+ }
+ boolean[] hasRecordedActivity = {false};
+ mRecordedWC.forAllActivities(activityRecord -> {
+ if (activityRecord.getUid() == uid && activityRecord.isVisibleRequested()) {
+ hasRecordedActivity[0] = true;
+ return true;
+ }
+ return false;
+ }, true /*traverseTopToBottom*/);
+ return hasRecordedActivity[0];
+ }
+
+ @GuardedBy("WindowManagerService.mGlobalLock")
+ private Set<Integer> getRecordedUids() {
+ Set<Integer> result = new ArraySet<>();
+ if (mRecordedWC == null) {
+ return result;
+ }
+ mRecordedWC.forAllActivities(activityRecord -> {
+ if (activityRecord.isVisibleRequested() && mLastInvokedStateByUid.containsKey(
+ activityRecord.getUid())) {
+ result.add(activityRecord.getUid());
+ }
+ }, true /*traverseTopToBottom*/);
+ return result;
+ }
+
+ @GuardedBy("WindowManagerService.mGlobalLock")
+ private void dispatchCallbacks(Set<Integer> uids, boolean visibleInScreenRecording) {
+ if (uids.isEmpty()) {
+ return;
+ }
+
+ for (Integer uid : uids) {
+ mLastInvokedStateByUid.put(uid, visibleInScreenRecording);
+ }
+
+ for (Callback callback : mCallbacks.values()) {
+ if (!uids.contains(callback.mUid)) {
+ continue;
+ }
+ try {
+ callback.mCallback.onScreenRecordingStateChanged(visibleInScreenRecording);
+ } catch (RemoteException e) {
+ // Client has died. Cleanup is handled via DeathRecipient.
+ }
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ pw.format("ScreenRecordingCallbackController:\n");
+ pw.format(" Registered callbacks:\n");
+ for (Map.Entry<IBinder, Callback> entry : mCallbacks.entrySet()) {
+ pw.format(" callback=%s uid=%s\n", entry.getKey(), entry.getValue().mUid);
+ }
+ pw.format(" Last invoked states:\n");
+ for (Map.Entry<Integer, Boolean> entry : mLastInvokedStateByUid.entrySet()) {
+ pw.format(" uid=%s isVisibleInScreenRecording=%s\n", entry.getKey(),
+ entry.getValue());
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b38dc00..9179acf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -303,6 +303,7 @@
import android.view.inputmethod.ImeTracker;
import android.window.AddToSurfaceSyncGroupResult;
import android.window.ClientWindowFrames;
+import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ITrustedPresentationListener;
@@ -1104,6 +1105,8 @@
void onAppFreezeTimeout();
}
+ private final ScreenRecordingCallbackController mScreenRecordingCallbackController;
+
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, WindowManagerPolicy policy,
ActivityTaskManagerService atm) {
@@ -1345,6 +1348,7 @@
mBlurController = new BlurController(mContext, mPowerManager);
mTaskFpsCallbackController = new TaskFpsCallbackController(mContext);
mAccessibilityController = new AccessibilityController(this);
+ mScreenRecordingCallbackController = new ScreenRecordingCallbackController(this);
mSystemPerformanceHinter = new SystemPerformanceHinter(mContext, displayId -> {
synchronized (mGlobalLock) {
DisplayContent dc = mRoot.getDisplayContent(displayId);
@@ -7188,6 +7192,7 @@
mSystemPerformanceHinter.dump(pw, "");
mTrustedPresentationListenerController.dump(pw);
mSensitiveContentPackages.dump(pw);
+ mScreenRecordingCallbackController.dump(pw);
}
}
@@ -9889,4 +9894,18 @@
int id) {
mTrustedPresentationListenerController.unregisterListener(listener, id);
}
+
+ @Override
+ public boolean registerScreenRecordingCallback(IScreenRecordingCallback callback) {
+ return mScreenRecordingCallbackController.register(callback);
+ }
+
+ @Override
+ public void unregisterScreenRecordingCallback(IScreenRecordingCallback callback) {
+ mScreenRecordingCallbackController.unregister(callback);
+ }
+
+ void onProcessActivityVisibilityChanged(int uid, boolean visible) {
+ mScreenRecordingCallbackController.onProcessActivityVisibilityChanged(uid, visible);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index b8fa5e5..6d2e8cc 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1271,8 +1271,10 @@
& (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
if (!wasAnyVisible && anyVisible) {
mAtm.mVisibleActivityProcessTracker.onAnyActivityVisible(this);
+ mAtm.mWindowManager.onProcessActivityVisibilityChanged(mUid, true /*visible*/);
} else if (wasAnyVisible && !anyVisible) {
mAtm.mVisibleActivityProcessTracker.onAllActivitiesInvisible(this);
+ mAtm.mWindowManager.onProcessActivityVisibilityChanged(mUid, false /*visible*/);
} else if (wasAnyVisible && !wasResumed && hasResumedActivity()) {
mAtm.mVisibleActivityProcessTracker.onActivityResumedWhileVisible(this);
}