Merge "Grant SYSTEM_ALERT_WINDOW during screen capture." into rvc-dev

This commit is contained in:
Alan Stokes
2020-05-12 15:19:45 +00:00
committed by Android (Google) Code Review
2 changed files with 57 additions and 3 deletions

View File

@@ -23,8 +23,6 @@ import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.projection.IMediaProjection;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -86,6 +84,12 @@ public final class MediaProjectionManager {
* capture request. Will be null if the result from the
* startActivityForResult() is anything other than RESULT_OK.
*
* Starting from Android {@link android.os.Build.VERSION_CODES#R}, if your application requests
* the {@link android.Manifest.permission#SYSTEM_ALERT_WINDOW} permission, and the
* user has not explicitly denied it, the permission will be automatically granted until the
* projection is stopped. This allows for user controls to be displayed on top of the screen
* being captured.
*
* @param resultCode The result code from {@link android.app.Activity#onActivityResult(int,
* int, android.content.Intent)}
* @param resultData The resulting data from {@link android.app.Activity#onActivityResult(int,

View File

@@ -22,6 +22,7 @@ import android.app.AppOpsManager;
import android.app.IProcessObserver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
@@ -43,6 +44,7 @@ import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -399,11 +401,12 @@ public final class MediaProjectionManagerService extends SystemService
public final UserHandle userHandle;
private final int mTargetSdkVersion;
private final boolean mIsPrivileged;
private final int mType;
private IMediaProjectionCallback mCallback;
private IBinder mToken;
private IBinder.DeathRecipient mDeathEater;
private int mType;
private boolean mRestoreSystemAlertWindow;
MediaProjection(int type, int uid, String packageName, int targetSdkVersion,
boolean isPrivileged) {
@@ -494,6 +497,35 @@ public final class MediaProjectionManagerService extends SystemService
"MediaProjectionCallbacks must be valid, aborting MediaProjection", e);
return;
}
if (mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE) {
final long token = Binder.clearCallingIdentity();
try {
// We allow an app running a current screen capture session to use
// SYSTEM_ALERT_WINDOW for the duration of the session, to enable
// them to overlay their UX on top of what is being captured.
// We only do this if the app requests the permission, and the appop
// is in its default state (the user has neither explicitly allowed nor
// disallowed it).
final PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(
packageName, PackageManager.GET_PERMISSIONS,
UserHandle.getUserId(uid));
if (ArrayUtils.contains(packageInfo.requestedPermissions,
Manifest.permission.SYSTEM_ALERT_WINDOW)) {
final int currentMode = mAppOps.unsafeCheckOpRawNoThrow(
AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, packageName);
if (currentMode == AppOpsManager.MODE_DEFAULT) {
mAppOps.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid,
packageName, AppOpsManager.MODE_ALLOWED);
mRestoreSystemAlertWindow = true;
}
}
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Package not found, aborting MediaProjection", e);
return;
} finally {
Binder.restoreCallingIdentity(token);
}
}
startProjectionLocked(this);
}
}
@@ -507,6 +539,24 @@ public final class MediaProjectionManagerService extends SystemService
+ "pid=" + Binder.getCallingPid() + ")");
return;
}
if (mRestoreSystemAlertWindow) {
final long token = Binder.clearCallingIdentity();
try {
// Put the appop back how it was, unless it has been changed from what
// we set it to.
// Note that WindowManager takes care of removing any existing overlay
// windows when we do this.
final int currentMode = mAppOps.unsafeCheckOpRawNoThrow(
AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, packageName);
if (currentMode == AppOpsManager.MODE_ALLOWED) {
mAppOps.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, packageName,
AppOpsManager.MODE_DEFAULT);
}
mRestoreSystemAlertWindow = false;
} finally {
Binder.restoreCallingIdentity(token);
}
}
stopProjectionLocked(this);
mToken.unlinkToDeath(mDeathEater, 0);
mToken = null;