Merge "Update ImageDecoder docs regarding Files" into rvc-dev
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c75870e..eea1d69 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -7513,7 +7513,15 @@
try {
super.rename(oldPath, newPath);
} catch (ErrnoException e) {
- if (e.errno == OsConstants.EXDEV && oldPath.startsWith("/storage/")) {
+ // On emulated volumes, we have bind mounts for /Android/data and
+ // /Android/obb, which prevents move from working across those directories
+ // and other directories on the filesystem. To work around that, try to
+ // recover by doing a copy instead.
+ // Note that we only do this for "/storage/emulated", because public volumes
+ // don't have these bind mounts, neither do private volumes that are not
+ // the primary storage.
+ if (e.errno == OsConstants.EXDEV && oldPath.startsWith("/storage/emulated")
+ && newPath.startsWith("/storage/emulated")) {
Log.v(TAG, "Recovering failed rename " + oldPath + " to " + newPath);
try {
Files.move(new File(oldPath).toPath(), new File(newPath).toPath(),
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 2f048c9..a50a522 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -68,6 +68,7 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Predicate;
import java.util.regex.Pattern;
/**
@@ -381,17 +382,51 @@
return result;
}
+ /**
+ * This method is similar to
+ * {@link DocumentsProvider#queryChildDocuments(String, String[], String)}. This method returns
+ * all children documents including hidden directories/files.
+ *
+ * <p>
+ * In a scoped storage world, access to "Android/data" style directories are hidden for privacy
+ * reasons. This method may show privacy sensitive data, so its usage should only be in
+ * restricted modes.
+ *
+ * @param parentDocumentId the directory to return children for.
+ * @param projection list of {@link Document} columns to put into the
+ * cursor. If {@code null} all supported columns should be
+ * included.
+ * @param sortOrder how to order the rows, formatted as an SQL
+ * {@code ORDER BY} clause (excluding the ORDER BY itself).
+ * Passing {@code null} will use the default sort order, which
+ * may be unordered. This ordering is a hint that can be used to
+ * prioritize how data is fetched from the network, but UI may
+ * always enforce a specific ordering
+ * @throws FileNotFoundException when parent document doesn't exist or query fails
+ */
+ protected Cursor queryChildDocumentsShowAll(
+ String parentDocumentId, String[] projection, String sortOrder)
+ throws FileNotFoundException {
+ return queryChildDocuments(parentDocumentId, projection, sortOrder, File -> true);
+ }
+
@Override
public Cursor queryChildDocuments(
String parentDocumentId, String[] projection, String sortOrder)
throws FileNotFoundException {
+ // Access to some directories is hidden for privacy reasons.
+ return queryChildDocuments(parentDocumentId, projection, sortOrder, this::shouldShow);
+ }
+ private Cursor queryChildDocuments(
+ String parentDocumentId, String[] projection, String sortOrder,
+ @NonNull Predicate<File> filter) throws FileNotFoundException {
final File parent = getFileForDocId(parentDocumentId);
final MatrixCursor result = new DirectoryCursor(
resolveProjection(projection), parentDocumentId, parent);
if (parent.isDirectory()) {
for (File file : FileUtils.listFilesOrEmpty(parent)) {
- if (!shouldHide(file)) {
+ if (filter.test(file)) {
includeFile(result, null, file);
}
}
@@ -617,6 +652,10 @@
return (PATTERN_HIDDEN_PATH.matcher(file.getAbsolutePath()).matches());
}
+ private boolean shouldShow(@NonNull File file) {
+ return !shouldHide(file);
+ }
+
protected boolean shouldBlockFromTree(@NonNull String docId) {
return false;
}
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index b5e2213..c4d27ec 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -23,8 +23,6 @@
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 @@
* 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,
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 0c70e10..8f919c3 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -275,6 +275,13 @@
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
}
+ @Override
+ public Cursor queryChildDocumentsForManage(
+ String parentDocId, String[] projection, String sortOrder)
+ throws FileNotFoundException {
+ return queryChildDocumentsShowAll(parentDocId, projection, sortOrder);
+ }
+
/**
* Check that the directory is the root of storage or blocked file from tree.
*
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 53f9ebc..1ed5cd8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2207,13 +2207,7 @@
@Override
public void setHdmiCecVolumeControlEnabled(final boolean isHdmiCecVolumeControlEnabled) {
enforceAccessPermission();
- runOnServiceThread(new Runnable() {
- @Override
- public void run() {
- HdmiControlService.this.setHdmiCecVolumeControlEnabled(
- isHdmiCecVolumeControlEnabled);
- }
- });
+ HdmiControlService.this.setHdmiCecVolumeControlEnabled(isHdmiCecVolumeControlEnabled);
}
@Override
@@ -3014,7 +3008,6 @@
}
void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
- assertRunOnServiceThread();
synchronized (mLock) {
mHdmiCecVolumeControlEnabled = isHdmiCecVolumeControlEnabled;
@@ -3030,7 +3023,6 @@
}
boolean isHdmiCecVolumeControlEnabled() {
- assertRunOnServiceThread();
synchronized (mLock) {
return mHdmiCecVolumeControlEnabled;
}
diff --git a/services/core/java/com/android/server/lights/TEST_MAPPING b/services/core/java/com/android/server/lights/TEST_MAPPING
new file mode 100644
index 0000000..f868ea0
--- /dev/null
+++ b/services/core/java/com/android/server/lights/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsHardwareTestCases",
+ "options": [
+ {"include-filter": "com.android.hardware.lights"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.lights"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ }
+ ]
+}
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 9e509f4..1a749b3 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -22,6 +22,7 @@
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.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 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 @@
"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 @@
+ "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;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 317bb43..d02be88 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -60,7 +60,37 @@
private final IntArray mShowingTransientTypes = new IntArray();
/** For resetting visibilities of insets sources. */
- private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() { };
+ private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() {
+
+ @Override
+ public void notifyInsetsControlChanged() {
+ boolean hasLeash = false;
+ final InsetsSourceControl[] controls =
+ mStateController.getControlsForDispatch(this);
+ if (controls == null) {
+ return;
+ }
+ for (InsetsSourceControl control : controls) {
+ final @InternalInsetsType int type = control.getType();
+ if (mShowingTransientTypes.indexOf(type) != -1) {
+ // The visibilities of transient bars will be handled with animations.
+ continue;
+ }
+ final SurfaceControl leash = control.getLeash();
+ if (leash != null) {
+ hasLeash = true;
+
+ // We use alpha to control the visibility here which aligns the logic at
+ // SurfaceAnimator.createAnimationLeash
+ mDisplayContent.getPendingTransaction().setAlpha(
+ leash, InsetsState.getDefaultVisibility(type) ? 1f : 0f);
+ }
+ }
+ if (hasLeash) {
+ mDisplayContent.scheduleAnimation();
+ }
+ }
+ };
private WindowState mFocusedWin;
private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);