Merge "Update TrustedPresentationListener docs and update exceptions" into main
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 0877bce..d13c4d7 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -97,7 +97,6 @@
"framework-minus-apex.ravenwood",
"hoststubgen-helper-runtime.ravenwood",
"hoststubgen-helper-framework-runtime.ravenwood",
- "core-libart-for-host",
"all-updatable-modules-system-stubs",
"junit",
"truth",
diff --git a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
index 10bfa42..426fcab 100644
--- a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
@@ -20,10 +20,12 @@
import android.graphics.Canvas;
import android.graphics.RecordingCanvas;
+import android.graphics.RectF;
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.text.NonEditableTextGenerator.TextType;
+import android.view.View;
import android.widget.TextView;
import androidx.test.InstrumentationRegistry;
@@ -79,7 +81,7 @@
mCached = cached;
mTextPaint = new TextPaint();
mTextPaint.setTextSize(10);
- mLineWidth = Integer.MAX_VALUE;
+ mLineWidth = 2048;
}
/**
@@ -106,7 +108,9 @@
state.resumeTiming();
textView.setText(text);
- textView.measure(AT_MOST | mLineWidth, UNSPECIFIED);
+ textView.measure(
+ View.MeasureSpec.makeMeasureSpec(mLineWidth, AT_MOST),
+ UNSPECIFIED);
}
}
@@ -129,10 +133,16 @@
while (state.keepRunning()) {
state.pauseTiming();
- final RecordingCanvas canvas = node.start(1200, 200);
- int save = canvas.save();
textView.setTextLocale(Locale.UK);
textView.setTextLocale(Locale.US);
+ textView.measure(
+ View.MeasureSpec.makeMeasureSpec(mLineWidth, AT_MOST),
+ UNSPECIFIED);
+ RectF bounds = textView.getLayout().computeDrawingBoundingBox();
+ final RecordingCanvas canvas = node.start(
+ (int) Math.ceil(bounds.width()),
+ (int) Math.ceil(bounds.height()));
+ int save = canvas.save();
if (!mCached) Canvas.freeTextLayoutCaches();
state.resumeTiming();
diff --git a/core/api/current.txt b/core/api/current.txt
index f41982f..b095501 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -41234,6 +41234,7 @@
method public void startAssistantActivity(@NonNull android.content.Intent, @NonNull android.os.Bundle);
method public void startVoiceActivity(android.content.Intent);
method public final void unregisterVisibleActivityCallback(@NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
+ field @FlaggedApi("android.service.voice.flags.allow_foreground_activities_in_on_show") public static final String KEY_FOREGROUND_ACTIVITIES = "android.service.voice.FOREGROUND_ACTIVITIES";
field public static final String KEY_SHOW_SESSION_ID = "android.service.voice.SHOW_SESSION_ID";
field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10
field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 1edf4bd..e14bf68 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -75,6 +75,7 @@
* {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
* Context.startActivity(Intent, Bundle)} and related methods.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ActivityOptions extends ComponentOptions {
private static final String TAG = "ActivityOptions";
@@ -527,6 +528,7 @@
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Context.class)
public static ActivityOptions makeCustomAnimation(Context context,
int enterResId, int exitResId) {
return makeCustomAnimation(context, enterResId, exitResId, 0, null, null);
@@ -547,6 +549,7 @@
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Context.class)
public static @NonNull ActivityOptions makeCustomAnimation(@NonNull Context context,
int enterResId, int exitResId, int backgroundColor) {
return makeCustomAnimation(context, enterResId, exitResId, backgroundColor, null, null);
@@ -572,6 +575,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Context.class)
public static ActivityOptions makeCustomAnimation(Context context,
int enterResId, int exitResId, int backgroundColor, Handler handler,
OnAnimationStartedListener listener) {
@@ -607,6 +611,7 @@
* @hide
*/
@TestApi
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Context.class)
public static @NonNull ActivityOptions makeCustomAnimation(@NonNull Context context,
int enterResId, int exitResId, int backgroundColor, @Nullable Handler handler,
@Nullable OnAnimationStartedListener startedListener,
@@ -641,6 +646,7 @@
*/
@RequiresPermission(START_TASKS_FROM_RECENTS)
@TestApi
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Context.class)
public static @NonNull ActivityOptions makeCustomTaskAnimation(@NonNull Context context,
int enterResId, int exitResId, @Nullable Handler handler,
@Nullable OnAnimationStartedListener startedListener,
@@ -663,6 +669,7 @@
* supply these options as the options Bundle when running an in-place animation.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Context.class)
public static ActivityOptions makeCustomInPlaceAnimation(Context context, int animId) {
if (animId == 0) {
throw new RuntimeException("You must specify a valid animation.");
@@ -769,6 +776,7 @@
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = View.class)
public static ActivityOptions makeScaleUpAnimation(View source,
int startX, int startY, int width, int height) {
ActivityOptions opts = new ActivityOptions();
@@ -797,6 +805,7 @@
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = View.class)
public static ActivityOptions makeClipRevealAnimation(View source,
int startX, int startY, int width, int height) {
ActivityOptions opts = new ActivityOptions();
@@ -842,6 +851,7 @@
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = View.class)
public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
Bitmap thumbnail, int startX, int startY) {
return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null);
@@ -864,11 +874,13 @@
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = View.class)
private static ActivityOptions makeThumbnailScaleUpAnimation(View source,
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true);
}
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = View.class)
private static ActivityOptions makeThumbnailAnimation(View source,
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
boolean scaleUp) {
@@ -890,6 +902,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Context.class)
public static ActivityOptions makeMultiThumbFutureAspectScaleAnimation(Context context,
Handler handler, IAppTransitionAnimationSpecsFuture specsFuture,
OnAnimationStartedListener listener, boolean scaleUp) {
@@ -922,6 +935,7 @@
* supply these options as the options Bundle when starting an activity.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = View.class)
public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
Handler handler, OnAnimationStartedListener listener) {
@@ -929,6 +943,7 @@
targetWidth, targetHeight, handler, listener, false);
}
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = View.class)
private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail,
int startX, int startY, int targetWidth, int targetHeight,
Handler handler, OnAnimationStartedListener listener, boolean scaleUp) {
@@ -948,6 +963,7 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = View.class)
public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
AppTransitionAnimationSpec[] specs, Handler handler,
OnAnimationStartedListener onAnimationStartedListener,
@@ -980,6 +996,7 @@
* @see android.transition.Transition#setEpicenterCallback(
* android.transition.Transition.EpicenterCallback)
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Activity.class)
public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
View sharedElement, String sharedElementName) {
return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName));
@@ -1005,6 +1022,7 @@
* android.transition.Transition.EpicenterCallback)
*/
@SafeVarargs
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Activity.class)
public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
Pair<View, String>... sharedElements) {
ActivityOptions opts = new ActivityOptions();
@@ -1031,6 +1049,7 @@
* @hide
*/
@SafeVarargs
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Window.class)
public static Pair<ActivityOptions, ExitTransitionCoordinator> startSharedElementAnimation(
Window window, ExitTransitionCallbacks exitCallbacks, SharedElementCallback callback,
Pair<View, String>... sharedElements) {
@@ -1052,6 +1071,7 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Window.class)
public static void stopSharedElementAnimation(Window window) {
final View decorView = window.getDecorView();
if (decorView == null) {
@@ -1069,6 +1089,7 @@
}
}
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Window.class)
static ExitTransitionCoordinator makeSceneTransitionAnimation(
ExitTransitionCallbacks exitCallbacks, SharedElementCallback callback, Window window,
ActivityOptions opts, Pair<View, String>[] sharedElements) {
@@ -1119,6 +1140,7 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = Activity.class)
static ActivityOptions makeSceneTransitionAnimation(Activity activity,
ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,
int resultCode, Intent resultData) {
@@ -1173,6 +1195,7 @@
*/
@RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = RemoteAnimationAdapter.class)
public static ActivityOptions makeRemoteAnimation(
RemoteAnimationAdapter remoteAnimationAdapter) {
final ActivityOptions opts = new ActivityOptions();
@@ -1187,6 +1210,7 @@
* @hide
*/
@RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = RemoteAnimationAdapter.class)
public static ActivityOptions makeRemoteAnimation(RemoteAnimationAdapter remoteAnimationAdapter,
RemoteTransition remoteTransition) {
final ActivityOptions opts = new ActivityOptions();
@@ -1202,6 +1226,7 @@
* @hide
*/
@RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = RemoteAnimationAdapter.class)
public static ActivityOptions makeRemoteTransition(RemoteTransition remoteTransition) {
final ActivityOptions opts = new ActivityOptions();
opts.mRemoteTransition = remoteTransition;
@@ -1216,6 +1241,7 @@
* picture-in-picture mode.
*/
@NonNull
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = PictureInPictureParams.class)
public static ActivityOptions makeLaunchIntoPip(
@NonNull PictureInPictureParams pictureInPictureParams) {
final ActivityOptions opts = new ActivityOptions();
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 41b4004..f727ee5 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -46,6 +46,7 @@
* {@link android.content.Context#sendBroadcast(android.content.Intent)
* Context.sendBroadcast(Intent)} and related methods.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BroadcastOptions extends ComponentOptions {
private @Flags int mFlags;
private long mTemporaryAppAllowlistDuration;
@@ -751,6 +752,7 @@
* @hide
*/
@TestApi
+ @android.ravenwood.annotation.RavenwoodThrow
public boolean testRequireCompatChange(int uid) {
if (mRequireCompatChangeId != CHANGE_INVALID) {
final boolean requireEnabled = (mFlags & FLAG_REQUIRE_COMPAT_CHANGE_ENABLED) != 0;
diff --git a/core/java/android/app/ComponentOptions.java b/core/java/android/app/ComponentOptions.java
index e0e2855..ce16ddf 100644
--- a/core/java/android/app/ComponentOptions.java
+++ b/core/java/android/app/ComponentOptions.java
@@ -34,6 +34,7 @@
@TestApi
// Suppressed since lint is recommending class have a suffix of Params.
@SuppressLint("UserHandleName")
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ComponentOptions {
/**
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 4dc32d5..61b52c6 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -800,7 +800,6 @@
*
* @hide
*/
- @android.ravenwood.annotation.RavenwoodReplace
public static void bytesToFile(String filename, byte[] content) throws IOException {
if (filename.startsWith("/proc/")) {
final int oldMask = StrictMode.allowThreadDiskWritesMask();
@@ -816,14 +815,6 @@
}
}
- /** @hide */
- public static void bytesToFile$ravenwood(String filename, byte[] content) throws IOException {
- // No StrictMode support, so we can just directly write
- try (FileOutputStream fos = new FileOutputStream(filename)) {
- fos.write(content);
- }
- }
-
/**
* Writes string to file. Basically same as "echo -n $string > $filename"
*
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 93d5082..6532d5c 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -40,14 +40,18 @@
import android.content.ContentResolver;
import android.net.Uri;
import android.os.MessageQueue.OnFileDescriptorEventListener;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodNativeSubstitutionClass;
+import android.ravenwood.annotation.RavenwoodReplace;
+import android.ravenwood.annotation.RavenwoodThrow;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
+import android.util.CloseGuard;
import android.util.Log;
import android.util.Slog;
-import dalvik.system.CloseGuard;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -70,6 +74,8 @@
* The FileDescriptor returned by {@link Parcel#readFileDescriptor}, allowing
* you to close it when done with it.
*/
+@RavenwoodKeepWholeClass
+@RavenwoodNativeSubstitutionClass("com.android.hoststubgen.nativesubstitution.ParcelFileDescriptor_host")
public class ParcelFileDescriptor implements Parcelable, Closeable {
private static final String TAG = "ParcelFileDescriptor";
@@ -197,11 +203,11 @@
}
mWrapped = null;
mFd = fd;
- IoUtils.setFdOwner(mFd, this);
+ setFdOwner(mFd);
mCommFd = commChannel;
if (mCommFd != null) {
- IoUtils.setFdOwner(mCommFd, this);
+ setFdOwner(mCommFd);
}
mGuard.open("close");
@@ -284,15 +290,17 @@
*/
// We can't accept a generic Executor here, since we need to use
// MessageQueue.addOnFileDescriptorEventListener()
+ @RavenwoodThrow(blockedBy = MessageQueue.class)
@SuppressLint("ExecutorRegistration")
public static @NonNull ParcelFileDescriptor wrap(@NonNull ParcelFileDescriptor pfd,
@NonNull Handler handler, @NonNull OnCloseListener listener) throws IOException {
final FileDescriptor original = new FileDescriptor();
- original.setInt$(pfd.detachFd());
+ setFdInt(original, pfd.detachFd());
return fromFd(original, handler, listener);
}
/** {@hide} */
+ @RavenwoodThrow(blockedBy = MessageQueue.class)
public static ParcelFileDescriptor fromFd(FileDescriptor fd, Handler handler,
final OnCloseListener listener) throws IOException {
if (handler == null) {
@@ -318,7 +326,7 @@
}
if (status != null) {
queue.removeOnFileDescriptorEventListener(fd);
- IoUtils.closeQuietly(fd);
+ closeInternal(fd);
listener.onClose(status.asIOException());
return 0;
}
@@ -329,6 +337,7 @@
return pfd;
}
+ @RavenwoodReplace
private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
if ((mode & MODE_WRITE_ONLY) != 0 && (mode & MODE_APPEND) == 0
&& (mode & MODE_TRUNCATE) == 0 && ((mode & MODE_READ_ONLY) == 0)
@@ -352,17 +361,38 @@
}
}
+ private static FileDescriptor openInternal$ravenwood(File file, int mode)
+ throws FileNotFoundException {
+ try {
+ return native_open$ravenwood(file, mode);
+ } catch (FileNotFoundException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ }
+
+ @RavenwoodReplace
+ private static void closeInternal(FileDescriptor fd) {
+ IoUtils.closeQuietly(fd);
+ }
+
+ private static void closeInternal$ravenwood(FileDescriptor fd) {
+ native_close$ravenwood(fd);
+ }
+
/**
* Create a new ParcelFileDescriptor that is a dup of an existing
* FileDescriptor. This obeys standard POSIX semantics, where the
* new file descriptor shared state such as file position with the
* original file descriptor.
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
try {
final FileDescriptor fd = new FileDescriptor();
int intfd = Os.fcntlInt(orig, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
- fd.setInt$(intfd);
+ setFdInt(fd, intfd);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
@@ -375,6 +405,7 @@
* new file descriptor shared state such as file position with the
* original file descriptor.
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public ParcelFileDescriptor dup() throws IOException {
if (mWrapped != null) {
return mWrapped.dup();
@@ -393,14 +424,15 @@
* @return Returns a new ParcelFileDescriptor holding a FileDescriptor
* for a dup of the given fd.
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor fromFd(int fd) throws IOException {
final FileDescriptor original = new FileDescriptor();
- original.setInt$(fd);
+ setFdInt(original, fd);
try {
final FileDescriptor dup = new FileDescriptor();
int intfd = Os.fcntlInt(original, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
- dup.setInt$(intfd);
+ setFdInt(dup, intfd);
return new ParcelFileDescriptor(dup);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
@@ -423,7 +455,7 @@
*/
public static ParcelFileDescriptor adoptFd(int fd) {
final FileDescriptor fdesc = new FileDescriptor();
- fdesc.setInt$(fd);
+ setFdInt(fdesc, fd);
return new ParcelFileDescriptor(fdesc);
}
@@ -452,6 +484,7 @@
*
* @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor fromSocket(Socket socket) {
FileDescriptor fd = socket.getFileDescriptor$();
try {
@@ -485,6 +518,7 @@
*
* @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) {
FileDescriptor fd = datagramSocket.getFileDescriptor$();
try {
@@ -499,6 +533,7 @@
* ParcelFileDescriptor in the returned array is the read side; the second
* is the write side.
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor[] createPipe() throws IOException {
try {
final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
@@ -520,6 +555,7 @@
* calling {@link #checkError()}, usually after detecting an EOF.
* This can also be used to detect remote crashes.
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor[] createReliablePipe() throws IOException {
try {
final FileDescriptor[] comm = createCommSocketPair();
@@ -536,6 +572,7 @@
* Create two ParcelFileDescriptors structured as a pair of sockets
* connected to each other. The two sockets are indistinguishable.
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor[] createSocketPair() throws IOException {
return createSocketPair(SOCK_STREAM);
}
@@ -543,6 +580,7 @@
/**
* @hide
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor[] createSocketPair(int type) throws IOException {
try {
final FileDescriptor fd0 = new FileDescriptor();
@@ -565,6 +603,7 @@
* calling {@link #checkError()}, usually after detecting an EOF.
* This can also be used to detect remote crashes.
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor[] createReliableSocketPair() throws IOException {
return createReliableSocketPair(SOCK_STREAM);
}
@@ -572,6 +611,7 @@
/**
* @hide
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor[] createReliableSocketPair(int type) throws IOException {
try {
final FileDescriptor[] comm = createCommSocketPair();
@@ -586,6 +626,7 @@
}
}
+ @RavenwoodThrow(reason = "Requires JNI support")
private static FileDescriptor[] createCommSocketPair() throws IOException {
try {
// Use SOCK_SEQPACKET so that we have a guarantee that the status
@@ -614,6 +655,7 @@
*/
@UnsupportedAppUsage
@Deprecated
+ @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException {
if (data == null) return null;
MemoryFile file = new MemoryFile(name, data.length);
@@ -669,9 +711,10 @@
* @hide
*/
@TestApi
+ @RavenwoodThrow(reason = "Requires kernel support")
public static File getFile(FileDescriptor fd) throws IOException {
try {
- final String path = Os.readlink("/proc/self/fd/" + fd.getInt$());
+ final String path = Os.readlink("/proc/self/fd/" + getFdInt(fd));
if (OsConstants.S_ISREG(Os.stat(path).st_mode)
|| OsConstants.S_ISCHR(Os.stat(path).st_mode)) {
return new File(path);
@@ -700,6 +743,7 @@
* Return the total size of the file representing this fd, as determined by
* {@code stat()}. Returns -1 if the fd is not a file.
*/
+ @RavenwoodThrow(reason = "Requires JNI support")
public long getStatSize() {
if (mWrapped != null) {
return mWrapped.getStatSize();
@@ -724,6 +768,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @RavenwoodThrow(reason = "Requires JNI support")
public long seekTo(long pos) throws IOException {
if (mWrapped != null) {
return mWrapped.seekTo(pos);
@@ -751,7 +796,7 @@
if (mClosed) {
throw new IllegalStateException("Already closed");
}
- return mFd.getInt$();
+ return getFdInt(mFd);
}
}
@@ -773,7 +818,7 @@
if (mClosed) {
throw new IllegalStateException("Already closed");
}
- int fd = IoUtils.acquireRawFd(mFd);
+ int fd = acquireRawFd(mFd);
writeCommStatusAndClose(Status.DETACHED, null);
mClosed = true;
mGuard.close();
@@ -832,7 +877,7 @@
}
// Status MUST be sent before closing actual descriptor
writeCommStatusAndClose(status, msg);
- IoUtils.closeQuietly(mFd);
+ closeInternal(mFd);
releaseResources();
}
@@ -899,7 +944,7 @@
}
} finally {
- IoUtils.closeQuietly(mCommFd);
+ closeInternal(mCommFd);
mCommFd = null;
}
}
@@ -991,6 +1036,7 @@
* take care of calling {@link ParcelFileDescriptor#close
* ParcelFileDescriptor.close()} for you when the stream is closed.
*/
+ @RavenwoodKeepWholeClass
public static class AutoCloseInputStream extends FileInputStream {
private final ParcelFileDescriptor mPfd;
@@ -1042,6 +1088,7 @@
* take care of calling {@link ParcelFileDescriptor#close
* ParcelFileDescriptor.close()} for you when the stream is closed.
*/
+ @RavenwoodKeepWholeClass
public static class AutoCloseOutputStream extends FileOutputStream {
private final ParcelFileDescriptor mPfd;
@@ -1232,10 +1279,58 @@
}
}
+ // These native methods are currently only implemented by Ravenwood, as it's the only
+ // mechanism we have to jump to our RavenwoodNativeSubstitutionClass
+ private static native void native_setFdInt$ravenwood(FileDescriptor fd, int fdInt);
+ private static native int native_getFdInt$ravenwood(FileDescriptor fd);
+ private static native FileDescriptor native_open$ravenwood(File file, int pfdMode)
+ throws IOException;
+ private static native void native_close$ravenwood(FileDescriptor fd);
+
+ @RavenwoodReplace
+ private static void setFdInt(FileDescriptor fd, int fdInt) {
+ fd.setInt$(fdInt);
+ }
+
+ private static void setFdInt$ravenwood(FileDescriptor fd, int fdInt) {
+ native_setFdInt$ravenwood(fd, fdInt);
+ }
+
+ @RavenwoodReplace
+ private static int getFdInt(FileDescriptor fd) {
+ return fd.getInt$();
+ }
+
+ private static int getFdInt$ravenwood(FileDescriptor fd) {
+ return native_getFdInt$ravenwood(fd);
+ }
+
+ @RavenwoodReplace
+ private void setFdOwner(FileDescriptor fd) {
+ IoUtils.setFdOwner(fd, this);
+ }
+
+ private void setFdOwner$ravenwood(FileDescriptor fd) {
+ // FD owners currently unsupported under Ravenwood; ignored
+ }
+
+ @RavenwoodReplace
+ private int acquireRawFd(FileDescriptor fd) {
+ return IoUtils.acquireRawFd(fd);
+ }
+
+ private int acquireRawFd$ravenwood(FileDescriptor fd) {
+ // FD owners currently unsupported under Ravenwood; return FD directly
+ return getFdInt(fd);
+
+ }
+
+ @RavenwoodThrow
private static boolean isAtLeastQ() {
return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q);
}
+ @RavenwoodThrow
private static int ifAtLeastQ(int value) {
return isAtLeastQ() ? value : 0;
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 8c8af0e..222c69c 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -77,6 +77,7 @@
import com.android.internal.os.RuntimeInit;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
+import com.android.internal.util.Preconditions;
import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
@@ -154,6 +155,7 @@
* android.os.Binder} calls, it's still ultimately a best effort mechanism. Notably, disk or network
* access from JNI calls won't necessarily trigger it.
*/
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
public final class StrictMode {
private static final String TAG = "StrictMode";
private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE);
@@ -1267,6 +1269,7 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodReplace
public static void setThreadPolicyMask(@ThreadPolicyMask int threadPolicyMask) {
// In addition to the Java-level thread-local in Dalvik's
// BlockGuard, we also need to keep a native thread-local in
@@ -1279,6 +1282,12 @@
Binder.setThreadStrictModePolicy(threadPolicyMask);
}
+ /** @hide */
+ public static void setThreadPolicyMask$ravenwood(@ThreadPolicyMask int threadPolicyMask) {
+ // Ravenwood currently doesn't support any detection modes
+ Preconditions.checkFlagsArgument(threadPolicyMask, 0);
+ }
+
// Sets the policy in Dalvik/libcore (BlockGuard)
private static void setBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) {
if (threadPolicyMask == 0) {
@@ -1321,6 +1330,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodReplace
public static @ThreadPolicyMask int getThreadPolicyMask() {
final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (policy instanceof AndroidBlockGuardPolicy) {
@@ -1330,6 +1340,12 @@
}
}
+ /** @hide */
+ public static @ThreadPolicyMask int getThreadPolicyMask$ravenwood() {
+ // Ravenwood currently doesn't support any detection modes
+ return 0;
+ }
+
/** Returns the current thread's policy. */
public static ThreadPolicy getThreadPolicy() {
// TODO: this was a last minute Gingerbread API change (to
@@ -1359,6 +1375,7 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static @ThreadPolicyMask int allowThreadDiskWritesMask() {
int oldPolicyMask = getThreadPolicyMask();
int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_WRITE | DETECT_THREAD_DISK_READ);
@@ -1383,6 +1400,7 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static @ThreadPolicyMask int allowThreadDiskReadsMask() {
int oldPolicyMask = getThreadPolicyMask();
int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_READ);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ce7a026..be9915c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11909,6 +11909,16 @@
"accessibility_magnification_two_finger_triple_tap_enabled";
/**
+ * For pinch to zoom anywhere feature.
+ *
+ * If true, you should be able to pinch to magnify the window anywhere.
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_PINCH_TO_ZOOM_ANYWHERE_ENABLED =
+ "accessibility_pinch_to_zoom_anywhere_enabled";
+
+ /**
* Controls magnification capability. Accessibility magnification is capable of at least one
* of the magnification modes.
*
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index d40b39e..d1368ca 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -20,6 +20,7 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -53,6 +54,7 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.service.voice.flags.Flags;
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.Log;
@@ -184,6 +186,17 @@
*/
public static final String KEY_SHOW_SESSION_ID = "android.service.voice.SHOW_SESSION_ID";
+ /**
+ * Bundle key used to specify foreground activity app components.
+ * <p>
+ * Type: ArrayList<ComponentName>
+ * </p>
+ * @see #onShow(Bundle, int)
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_FOREGROUND_ACTIVITIES_IN_ON_SHOW)
+ public static final String KEY_FOREGROUND_ACTIVITIES =
+ "android.service.voice.FOREGROUND_ACTIVITIES";
+
final Context mContext;
final HandlerCaller mHandlerCaller;
@@ -1803,14 +1816,39 @@
*
* @param args The arguments that were supplied to
* {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
- * Some example keys include : "invocation_type", "invocation_phone_state",
- * {@link #KEY_SHOW_SESSION_ID}, "invocation_time_ms",
- * Intent.EXTRA_TIME ("android.intent.extra.TIME") indicating timing
- * in milliseconds of the KeyEvent that triggered Assistant and
- * Intent.EXTRA_ASSIST_INPUT_DEVICE_ID (android.intent.extra.ASSIST_INPUT_DEVICE_ID)
- * referring to the device that sent the request. Starting from Android 14, the system will
- * add {@link #KEY_SHOW_SESSION_ID}, the Bundle is not null. But the
- * application should handle null case before Android 14.
+ * Some example keys include :
+ * <ul>
+ * <li>
+ * invocation_type
+ * </li>
+ * <li>
+ * invocation_phone_state
+ * </li>
+ * <li>
+ * {@link #KEY_SHOW_SESSION_ID}
+ * </li>
+ * <li>
+ * invocation_time_ms
+ * </li>
+ * <li>
+ * Intent.EXTRA_TIME ("android.intent.extra.TIME") indicating timing in milliseconds of
+ * the KeyEvent that triggered Assistant
+ * </li>
+ * <li>
+ * Intent.EXTRA_ASSIST_INPUT_DEVICE_ID (android.intent.extra.ASSIST_INPUT_DEVICE_ID)
+ * referring to the device that sent the request
+ * </li>
+ * <li>
+ * {@link #KEY_FOREGROUND_ACTIVITIES} provides foreground activities of up coming
+ * onHandleAssist/onHandleScreenshot calls earlier. This is only populated if session
+ * was requested with {@link VoiceInteractionSession.SHOW_WITH_ASSIST} show flag.
+ * </li>
+ * <li>
+ * Starting from Android 14, the system will add {@link #KEY_SHOW_SESSION_ID}, the
+ * Bundle is not null. But the application should handle null case before Android 14.
+ * </li>
+ * </ul>
+ *
* @param showFlags The show flags originally provided to
* {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
*/
diff --git a/core/java/android/service/voice/flags/flags.aconfig b/core/java/android/service/voice/flags/flags.aconfig
index b596666..1c8752b 100644
--- a/core/java/android/service/voice/flags/flags.aconfig
+++ b/core/java/android/service/voice/flags/flags.aconfig
@@ -13,3 +13,10 @@
description: "This flag allows hotword detection service to egress reason code for hotword bump."
bug: "290951024"
}
+
+flag {
+ name: "allow_foreground_activities_in_on_show"
+ namespace: "voice_interaction_session"
+ description: "This flag allows providing foreground app component along with onShow args."
+ bug: "319409708"
+}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 09f15c3..95d1974 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -116,9 +116,6 @@
// This is empty if mLtrWithoutBidi is true.
private @NonNull ByteArray mLevels = new ByteArray();
- // The bidi level for runs.
- private @NonNull ByteArray mRunLevels = new ByteArray();
-
private Bidi mBidi;
// The whole width of the text.
@@ -154,7 +151,6 @@
reset();
mLevels.clearWithReleasingLargeArray();
mWidths.clearWithReleasingLargeArray();
- mRunLevels.clearWithReleasingLargeArray();
mFontMetrics.clearWithReleasingLargeArray();
mSpanEndCache.clearWithReleasingLargeArray();
}
@@ -167,7 +163,6 @@
mCopiedBuffer = null;
mWholeWidth = 0;
mLevels.clear();
- mRunLevels.clear();
mWidths.clear();
mFontMetrics.clear();
mSpanEndCache.clear();
@@ -250,8 +245,7 @@
}
// Reorder directionality run visually.
- mRunLevels.resize(bidi.getRunCount());
- byte[] levels = mRunLevels.getRawArray();
+ byte[] levels = new byte[bidi.getRunCount()];
for (int i = 0; i < bidi.getRunCount(); ++i) {
levels[i] = (byte) bidi.getRunLevel(i);
}
diff --git a/core/java/android/util/CloseGuard.java b/core/java/android/util/CloseGuard.java
index ba504a3..efe4132 100644
--- a/core/java/android/util/CloseGuard.java
+++ b/core/java/android/util/CloseGuard.java
@@ -17,6 +17,8 @@
package android.util;
import android.annotation.NonNull;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodReplace;
/**
* CloseGuard is a mechanism for flagging implicit finalizer cleanup of
@@ -108,15 +110,35 @@
* in a method, the call to {@code open} should occur just after
* resource acquisition.
*/
+@RavenwoodKeepWholeClass
public final class CloseGuard {
private final dalvik.system.CloseGuard mImpl;
/**
* Constructs a new CloseGuard instance.
* {@link #open(String)} can be used to set up the instance to warn on failure to close.
+ *
+ * @hide
+ */
+ public static CloseGuard get() {
+ return new CloseGuard();
+ }
+
+ /**
+ * Constructs a new CloseGuard instance.
+ * {@link #open(String)} can be used to set up the instance to warn on failure to close.
*/
public CloseGuard() {
- mImpl = dalvik.system.CloseGuard.get();
+ mImpl = getImpl();
+ }
+
+ @RavenwoodReplace
+ private dalvik.system.CloseGuard getImpl() {
+ return dalvik.system.CloseGuard.get();
+ }
+
+ private dalvik.system.CloseGuard getImpl$ravenwood() {
+ return null;
}
/**
@@ -127,12 +149,16 @@
* @throws NullPointerException if closeMethodName is null.
*/
public void open(@NonNull String closeMethodName) {
- mImpl.open(closeMethodName);
+ if (mImpl != null) {
+ mImpl.open(closeMethodName);
+ }
}
/** Marks this CloseGuard instance as closed to avoid warnings on finalization. */
public void close() {
- mImpl.close();
+ if (mImpl != null) {
+ mImpl.close();
+ }
}
/**
@@ -140,6 +166,8 @@
* before finalization.
*/
public void warnIfOpen() {
- mImpl.warnIfOpen();
+ if (mImpl != null) {
+ mImpl.warnIfOpen();
+ }
}
}
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 4a3b8ac..de809c8 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -168,7 +168,8 @@
statsToken = ImeTracker.forLogging().onRequestHide(null /* component */,
Process.myUid(),
ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
+ mController.getHost().isHandlingPointerEvent() /* fromUser */);
}
ImeTracker.forLogging().onProgress(statsToken,
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index dd09157..6b7f9db 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -224,6 +224,11 @@
* @param running {@code true} if there is any animation running; {@code false} otherwise.
*/
default void notifyAnimationRunningStateChanged(boolean running) {}
+
+ /** @see ViewRootImpl#isHandlingPointerEvent */
+ default boolean isHandlingPointerEvent() {
+ return false;
+ }
}
private static final String TAG = "InsetsController";
@@ -1063,7 +1068,8 @@
if ((types & ime()) != 0) {
statsToken = ImeTracker.forLogging().onRequestShow(null /* component */,
Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
- SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API);
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API,
+ mHost.isHandlingPointerEvent() /* fromUser */);
}
show(types, false /* fromIme */, statsToken);
@@ -1168,7 +1174,8 @@
if ((types & ime()) != 0) {
statsToken = ImeTracker.forLogging().onRequestHide(null /* component */,
Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
+ mHost.isHandlingPointerEvent() /* fromUser */);
}
hide(types, false /* fromIme */, statsToken);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e03f857..32e6069 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7587,6 +7587,15 @@
}
}
+ /**
+ * Returns whether this view is currently handling a pointer event.
+ *
+ * @hide
+ */
+ public boolean isHandlingPointerEvent() {
+ return mAttachInfo.mHandlingPointerEvent;
+ }
+
private void resetPointerIcon(MotionEvent event) {
mPointerIconType = null;
mResolvedPointerIcon = null;
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index 40730e8..f2a3b4c 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -286,6 +286,11 @@
}
}
+ @Override
+ public boolean isHandlingPointerEvent() {
+ return mViewRoot != null && mViewRoot.isHandlingPointerEvent();
+ }
+
private boolean isVisibleToUser() {
return mViewRoot.getHostVisibility() == View.VISIBLE;
}
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index 3e7a9cb..0b3581e 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -25,3 +25,20 @@
bug: "305193969"
is_fixed_read_only: true
}
+
+flag {
+ name: "sensitive_content_app_protection_api"
+ namespace: "permissions"
+ description: "This flag controls the new sensitive content protection API,"
+ " The API will be used by other ui toolkits (i.e. compose, webview, custom virtual views)."
+ bug: "322887144"
+}
+
+flag {
+ name: "sensitive_content_app_protection"
+ namespace: "permissions"
+ description: "This flag controls the sensitive content protection when sharing the screen"
+ bug: "322887144"
+ # Referenced in WM where WM starts before DeviceConfig
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index c244287..89da041 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -575,14 +575,14 @@
@AnyThread
@NonNull
static ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+ @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
final IImeTracker service = getImeTrackerService();
if (service == null) {
// Create token with "fake" binder if the service was not found.
return new ImeTracker.Token(new Binder(), tag);
}
try {
- return service.onRequestShow(tag, uid, origin, reason);
+ return service.onRequestShow(tag, uid, origin, reason, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -592,14 +592,14 @@
@AnyThread
@NonNull
static ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+ @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
final IImeTracker service = getImeTrackerService();
if (service == null) {
// Create token with "fake" binder if the service was not found.
return new ImeTracker.Token(new Binder(), tag);
}
try {
- return service.onRequestHide(tag, uid, origin, reason);
+ return service.onRequestHide(tag, uid, origin, reason, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index 1b7d57b..31c0363 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -37,6 +37,7 @@
import android.util.Log;
import android.view.InsetsController.AnimationType;
import android.view.SurfaceControl;
+import android.view.View;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.SoftInputShowHideReason;
@@ -325,12 +326,22 @@
* @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME show request.
* @param reason the reason why the IME show request was created.
+ * @param fromUser whether this request was created directly from user interaction.
*
* @return An IME tracking token.
*/
@NonNull
Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
- @SoftInputShowHideReason int reason);
+ @SoftInputShowHideReason int reason, boolean fromUser);
+
+ /**
+ * Alias for {@link #onRequestShow(String, int, int, int, boolean)} with
+ * {@code fromUser} set to {@code false}.
+ */
+ default Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
+ @SoftInputShowHideReason int reason) {
+ return onRequestShow(component, uid, origin, reason, false /* fromUser */);
+ }
/**
* Creates an IME hide request tracking token.
@@ -340,12 +351,22 @@
* @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME hide request.
* @param reason the reason why the IME hide request was created.
+ * @param fromUser whether this request was created directly from user interaction.
*
* @return An IME tracking token.
*/
@NonNull
Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
- @SoftInputShowHideReason int reason);
+ @SoftInputShowHideReason int reason, boolean fromUser);
+
+ /**
+ * Alias for {@link #onRequestHide(String, int, int, int, boolean)} with
+ * {@code fromUser} set to {@code false}.
+ */
+ default Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
+ @SoftInputShowHideReason int reason) {
+ return onRequestHide(component, uid, origin, reason, false /* fromUser */);
+ }
/**
* Called when an IME request progresses to a further phase.
@@ -394,6 +415,28 @@
void onHidden(@Nullable Token token);
/**
+ * Returns whether the current IME request was created due to a user interaction. This can
+ * only be {@code true} when running on the view's UI thread.
+ *
+ * @param view the view for which the IME was requested.
+ * @return {@code true} if this request is coming from a user interaction,
+ * {@code false} otherwise.
+ */
+ static boolean isFromUser(@Nullable View view) {
+ if (view == null) {
+ return false;
+ }
+ final var handler = view.getHandler();
+ // Early return if not on the UI thread, to ensure safe access to getViewRootImpl() below.
+ if (handler == null || handler.getLooper() == null
+ || !handler.getLooper().isCurrentThread()) {
+ return false;
+ }
+ final var viewRootImpl = view.getViewRootImpl();
+ return viewRootImpl != null && viewRootImpl.isHandlingPointerEvent();
+ }
+
+ /**
* Get the singleton request tracker instance.
*
* @return the singleton request tracker instance
@@ -450,13 +493,14 @@
@NonNull
@Override
public Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean fromUser) {
final var tag = getTag(component);
final var token = IInputMethodManagerGlobalInvoker.onRequestShow(tag, uid, origin,
- reason);
+ reason, fromUser);
Log.i(TAG, token.mTag + ": onRequestShow at " + Debug.originToString(origin)
- + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason),
+ + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)
+ + " fromUser " + fromUser,
mLogStackTrace ? new Throwable() : null);
return token;
@@ -465,13 +509,14 @@
@NonNull
@Override
public Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean fromUser) {
final var tag = getTag(component);
final var token = IInputMethodManagerGlobalInvoker.onRequestHide(tag, uid, origin,
- reason);
+ reason, fromUser);
Log.i(TAG, token.mTag + ": onRequestHide at " + Debug.originToString(origin)
- + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason),
+ + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)
+ + " fromUser " + fromUser,
mLogStackTrace ? new Throwable() : null);
return token;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 3bc02a6..3d70c5b 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2141,8 +2141,10 @@
@ShowFlags int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
if (statsToken == null) {
+ // TODO(b/303041796): handle tracking physical keyboard and DPAD as user interactions
statsToken = ImeTracker.forLogging().onRequestShow(null /* component */,
- Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason);
+ Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason,
+ ImeTracker.isFromUser(view));
}
ImeTracker.forLatency().onRequestShow(statsToken, ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
reason, ActivityThread::currentApplication);
@@ -2291,15 +2293,22 @@
private boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ // Get served view initially for statsToken creation.
+ final View initialServedView;
+ synchronized (mH) {
+ initialServedView = getServedViewLocked();
+ }
+
final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
- null /* component */, Process.myUid(),
- ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, reason);
+ null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
+ reason, ImeTracker.isFromUser(initialServedView));
ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
reason, ActivityThread::currentApplication);
ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow",
this, null /* icProto */);
checkFocus();
synchronized (mH) {
+ // Get served view again in case it changed between the synchronized blocks.
final View servedView = getServedViewLocked();
if (servedView == null || servedView.getWindowToken() != windowToken) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
@@ -2335,8 +2344,8 @@
final var reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW;
final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
- null /* component */, Process.myUid(),
- ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, reason);
+ null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
+ reason, ImeTracker.isFromUser(view));
ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
reason, ActivityThread::currentApplication);
ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromView",
diff --git a/core/java/com/android/internal/inputmethod/IImeTracker.aidl b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
index c7418ee..2759043 100644
--- a/core/java/com/android/internal/inputmethod/IImeTracker.aidl
+++ b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
@@ -31,9 +31,10 @@
* @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME show request.
* @param reason the reason why the IME show request was created.
+ * @param fromUser whether this request was created directly from user interaction.
* @return A new IME tracking token.
*/
- ImeTracker.Token onRequestShow(String tag, int uid, int origin, int reason);
+ ImeTracker.Token onRequestShow(String tag, int uid, int origin, int reason, boolean fromUser);
/**
* Called when an IME hide request is created.
@@ -42,9 +43,10 @@
* @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME hide request.
* @param reason the reason why the IME hide request was created.
+ * @param fromUser whether this request was created directly from user interaction.
* @return A new IME tracking token.
*/
- ImeTracker.Token onRequestHide(String tag, int uid, int origin, int reason);
+ ImeTracker.Token onRequestHide(String tag, int uid, int origin, int reason, boolean fromUser);
/**
* Called when the IME request progresses to a further phase.
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 7b3565b..48c455a 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -28,7 +28,7 @@
/** @hide */
public class Cuj {
@VisibleForTesting
- public static final int MAX_LENGTH_OF_CUJ_NAME = 80;
+ public static final int MAX_LENGTH_OF_CUJ_NAME = 82;
// Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
@@ -122,10 +122,12 @@
public static final int CUJ_PREDICTIVE_BACK_HOME = 86;
public static final int CUJ_LAUNCHER_SEARCH_QSB_OPEN = 87;
public static final int CUJ_BACK_PANEL_ARROW = 88;
+ public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK = 89;
+ public static final int CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH = 90;
// When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
@VisibleForTesting
- static final int LAST_CUJ = CUJ_BACK_PANEL_ARROW;
+ static final int LAST_CUJ = CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH;
/** @hide */
@IntDef({
@@ -209,6 +211,8 @@
CUJ_PREDICTIVE_BACK_HOME,
CUJ_LAUNCHER_SEARCH_QSB_OPEN,
CUJ_BACK_PANEL_ARROW,
+ CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK,
+ CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -302,6 +306,8 @@
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BACK_PANEL_ARROW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BACK_PANEL_ARROW;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_BACK;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_WEB_SEARCH;
}
private Cuj() {
@@ -478,6 +484,10 @@
return "LAUNCHER_SEARCH_QSB_OPEN";
case CUJ_BACK_PANEL_ARROW:
return "BACK_PANEL_ARROW";
+ case CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK:
+ return "LAUNCHER_CLOSE_ALL_APPS_BACK";
+ case CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH:
+ return "LAUNCHER_SEARCH_QSB_WEB_SEARCH";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/os/ProcStatsUtil.java b/core/java/com/android/internal/os/ProcStatsUtil.java
index b67190b..1d8cf83 100644
--- a/core/java/com/android/internal/os/ProcStatsUtil.java
+++ b/core/java/com/android/internal/os/ProcStatsUtil.java
@@ -93,23 +93,17 @@
* seen, or at the end of the file
*/
@Nullable
- @android.ravenwood.annotation.RavenwoodReplace
public static String readTerminatedProcFile(String path, byte terminator) {
// Permit disk reads here, as /proc isn't really "on disk" and should be fast.
// TODO: make BlockGuard ignore /proc/ and /sys/ files perhaps?
- final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
+ final int savedPolicy = StrictMode.allowThreadDiskReadsMask();
try {
return readTerminatedProcFileInternal(path, terminator);
} finally {
- StrictMode.setThreadPolicy(savedPolicy);
+ StrictMode.setThreadPolicyMask(savedPolicy);
}
}
- public static String readTerminatedProcFile$ravenwood(String path, byte terminator) {
- // No StrictMode under Ravenwood
- return readTerminatedProcFileInternal(path, terminator);
- }
-
private static String readTerminatedProcFileInternal(String path, byte terminator) {
try (FileInputStream is = new FileInputStream(path)) {
ByteArrayOutputStream byteStream = null;
diff --git a/core/java/com/android/internal/os/StoragedUidIoStatsReader.java b/core/java/com/android/internal/os/StoragedUidIoStatsReader.java
index 2d485da..fb6e133d 100644
--- a/core/java/com/android/internal/os/StoragedUidIoStatsReader.java
+++ b/core/java/com/android/internal/os/StoragedUidIoStatsReader.java
@@ -74,7 +74,6 @@
*
* @param callback The callback to invoke for each line of the proc file.
*/
- @android.ravenwood.annotation.RavenwoodReplace
public void readAbsolute(Callback callback) {
final int oldMask = StrictMode.allowThreadDiskReadsMask();
try {
@@ -84,10 +83,6 @@
}
}
- public void readAbsolute$ravenwood(Callback callback) {
- readAbsoluteInternal(callback);
- }
-
private void readAbsoluteInternal(Callback callback) {
File file = new File(sUidIoFile);
try (BufferedReader reader = Files.newBufferedReader(file.toPath())) {
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 10f75d0..c62e536 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -100,6 +100,7 @@
optional SettingProto accessibility_force_invert_color_enabled = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_magnification_two_finger_triple_tap_enabled = 53 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto qs_targets = 54 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto accessibility_pinch_to_zoom_anywhere_enabled = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/tests/coretests/src/android/os/StrictModeTest.java b/core/tests/coretests/src/android/os/StrictModeTest.java
new file mode 100644
index 0000000..9050583
--- /dev/null
+++ b/core/tests/coretests/src/android/os/StrictModeTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 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.os;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+public class StrictModeTest {
+ private File mFile;
+
+ @Before
+ public void setUp() throws Exception {
+ mFile = File.createTempFile("StrictModeTest", "tmp");
+ }
+
+ @Test
+ public void testAllowThreadDiskReadsMask() throws Exception {
+ final int mask = StrictMode.allowThreadDiskReadsMask();
+ try {
+ mFile.exists();
+ } finally {
+ StrictMode.setThreadPolicyMask(mask);
+ }
+ }
+
+ @Test
+ public void testAllowThreadDiskWritesMask() throws Exception {
+ final int mask = StrictMode.allowThreadDiskReadsMask();
+ try {
+ mFile.delete();
+ } finally {
+ StrictMode.setThreadPolicyMask(mask);
+ }
+ }
+
+ @Test
+ public void testThreadMask() throws Exception {
+ StrictMode.setThreadPolicyMask(0);
+ assertEquals(0, StrictMode.getThreadPolicyMask());
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 8310333..27398ea 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -31,10 +31,13 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,6 +46,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BinderDeathDispatcherTest {
+ @Rule
+ public RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
+
private static class MyTarget implements IInterface, IBinder {
public boolean isAlive = true;
public DeathRecipient mRecipient;
@@ -195,6 +201,7 @@
}
@Test
+ @DisabledOnRavenwood(reason = "b/324433654 -- depends on unsupported classes")
public void testRegisterAndKill() {
BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
@@ -265,6 +272,7 @@
}
@Test
+ @DisabledOnRavenwood(reason = "b/324433654 -- depends on unsupported classes")
public void duplicateRegistrations() {
BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
diff --git a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
index 80061a5..cc6c4e8 100644
--- a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
@@ -20,12 +20,15 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.os.FileUtils;
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -44,6 +47,8 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StoragedUidIoStatsReaderTest {
+ @Rule
+ public RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
private File mTestDir;
private File mTestFile;
@@ -79,6 +84,7 @@
* Tests that reading a file with 3 uids works as expected.
*/
@Test
+ @DisabledOnRavenwood(reason = "b/324433654 -- depends on unsupported classes")
public void testReadExpected() throws Exception {
BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath());
int[] uids = {0, 100, 200};
@@ -116,6 +122,7 @@
* Tests that a line with less than 11 items is passed over.
*/
@Test
+ @DisabledOnRavenwood(reason = "b/324433654 -- depends on unsupported classes")
public void testLineDoesNotElevenEntries() throws Exception {
BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath());
@@ -139,6 +146,7 @@
* Tests that a line that is malformed is passed over.
*/
@Test
+ @DisabledOnRavenwood(reason = "b/324433654 -- depends on unsupported classes")
public void testLineIsMalformed() throws Exception {
BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath());
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index dc8116d..cdcf8e4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -226,6 +226,7 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
+ Settings.Secure.ACCESSIBILITY_PINCH_TO_ZOOM_ANYWHERE_ENABLED,
Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
Settings.Secure.NOTIFICATION_BUBBLES,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index fabdafc..35d45a9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -319,6 +319,7 @@
VALIDATORS.put(
Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ACCESSIBILITY_PINCH_TO_ZOOM_ANYWHERE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index bc07836..612badd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1861,6 +1861,10 @@
SecureSettingsProto.Accessibility
.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_PINCH_TO_ZOOM_ANYWHERE_ENABLED,
+ SecureSettingsProto.Accessibility
+ .ACCESSIBILITY_PINCH_TO_ZOOM_ANYWHERE_ENABLED);
+ dumpSetting(s, p,
Settings.Secure.HEARING_AID_RINGTONE_ROUTING,
SecureSettingsProto.Accessibility.HEARING_AID_RINGTONE_ROUTING);
dumpSetting(s, p,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index f3d0d2c..97db9b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
@@ -65,7 +66,9 @@
controller.setOverExpansion(0f)
controller.setOverScrollAmount(0)
- controller.updateFooter()
+ if (!FooterViewRefactor.isEnabled) {
+ controller.updateFooter()
+ }
}
}
}
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index 16f99e9..0229611 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -142,6 +142,8 @@
class android.telephony.PinResult stubclass
# Just enough to support mocking, no further functionality
+class android.content.BroadcastReceiver stub
+ method <init> ()V stub
class android.content.Context stub
method <init> ()V stub
class android.content.pm.PackageManager stub
diff --git a/ravenwood/junit-src/android/platform/test/annotations/DisabledOnRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnRavenwood.java
index 4bf0980..1adb0f3 100644
--- a/ravenwood/junit-src/android/platform/test/annotations/DisabledOnRavenwood.java
+++ b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnRavenwood.java
@@ -42,4 +42,13 @@
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DisabledOnRavenwood {
+ /**
+ * One or more classes that aren't yet supported by Ravenwood, which this test depends on.
+ */
+ Class<?>[] blockedBy() default {};
+
+ /**
+ * General free-form description of why this test is being ignored.
+ */
+ String reason() default "";
}
diff --git a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
index 916dd59..7faa654 100644
--- a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
+++ b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
@@ -35,9 +35,12 @@
* to be enabled.
*
* @hide
+ *
+ * @deprecated Use {@link DisabledOnRavenwood} instead.
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
+@Deprecated
public @interface IgnoreUnderRavenwood {
/**
* One or more classes that aren't yet supported by Ravenwood, which this test depends on.
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 75c5a49..01c0074 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -21,6 +21,7 @@
com.android.internal.power.ModemPowerProfile
android.util.AtomicFile
+android.util.CloseGuard
android.util.DataUnit
android.util.DayOfMonthCursor
android.util.DumpableContainer
@@ -68,10 +69,14 @@
android.os.MessageQueue
android.os.PackageTagsList
android.os.Parcel
+android.os.ParcelFileDescriptor
+android.os.ParcelFileDescriptor$AutoCloseInputStream
+android.os.ParcelFileDescriptor$AutoCloseOutputStream
android.os.Parcelable
android.os.PowerComponents
android.os.Process
android.os.ServiceSpecificException
+android.os.StrictMode
android.os.SystemClock
android.os.SystemProperties
android.os.TestLooperManager
@@ -147,6 +152,9 @@
android.content.ContentProvider
android.app.ActivityManager
+android.app.ActivityOptions
+android.app.BroadcastOptions
+android.app.ComponentOptions
android.app.Instrumentation
android.metrics.LogMaker
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index e923e30a..bdc4a7a 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -38,6 +38,7 @@
per-file PackageWatchdog.java, RescueParty.java = file:/services/core/java/com/android/server/rollback/OWNERS
per-file PinnerService.java = file:/core/java/android/app/pinner/OWNERS
per-file RescueParty.java = shuc@google.com, ancr@google.com, harshitmahajan@google.com
+per-file SensitiveContentProtectionManagerService.java = file:/core/java/android/permission/OWNERS
per-file SystemClockTime.java = file:/services/core/java/com/android/server/timedetector/OWNERS
per-file SystemTimeZone.java = file:/services/core/java/com/android/server/timezonedetector/OWNERS
per-file TelephonyRegistry.java = file:/telephony/OWNERS
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index 27e1b9a..d06c31c 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -76,11 +76,11 @@
@NonNull
@Override
public ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+ @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
final var binder = new Binder();
final var token = new ImeTracker.Token(binder, tag);
final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_SHOW, ImeTracker.STATUS_RUN,
- origin, reason);
+ origin, reason, fromUser);
synchronized (mLock) {
mHistory.addEntry(binder, entry);
@@ -98,11 +98,11 @@
@NonNull
@Override
public ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+ @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
final var binder = new Binder();
final var token = new ImeTracker.Token(binder, tag);
final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_HIDE, ImeTracker.STATUS_RUN,
- origin, reason);
+ origin, reason, fromUser);
synchronized (mLock) {
mHistory.addEntry(binder, entry);
@@ -269,7 +269,7 @@
// Log newly finished entry.
FrameworkStatsLog.write(FrameworkStatsLog.IME_REQUEST_FINISHED, entry.mUid,
entry.mDuration, entry.mType, entry.mStatus, entry.mReason,
- entry.mOrigin, entry.mPhase);
+ entry.mOrigin, entry.mPhase, entry.mFromUser);
}
/** Dumps the contents of the circular buffer. */
@@ -353,6 +353,9 @@
@ImeTracker.Phase
private int mPhase = ImeTracker.PHASE_NOT_SET;
+ /** Whether this request was created directly from a user interaction. */
+ private final boolean mFromUser;
+
/**
* Name of the window that created the IME request.
*
@@ -363,13 +366,14 @@
private Entry(@NonNull String tag, int uid, @ImeTracker.Type int type,
@ImeTracker.Status int status, @ImeTracker.Origin int origin,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean fromUser) {
mTag = tag;
mUid = uid;
mType = type;
mStatus = status;
mOrigin = origin;
mReason = reason;
+ mFromUser = fromUser;
}
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3bd1e1a..5ab9151 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3529,7 +3529,7 @@
// Create statsToken is none exists.
if (statsToken == null) {
statsToken = createStatsTokenForFocusedClient(true /* show */,
- ImeTracker.ORIGIN_SERVER_START_INPUT, reason);
+ ImeTracker.ORIGIN_SERVER_START_INPUT, reason, false /* fromUser */);
}
if (!mVisibilityStateComputer.onImeShowFlags(statsToken, flags)) {
@@ -3605,8 +3605,9 @@
@SoftInputShowHideReason int reason) {
// Create statsToken is none exists.
if (statsToken == null) {
+ final boolean fromUser = reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY;
statsToken = createStatsTokenForFocusedClient(false /* show */,
- ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason);
+ ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason, fromUser);
}
if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) {
@@ -6675,10 +6676,11 @@
* @param show whether this is a show or a hide request.
* @param origin the origin of the IME request.
* @param reason the reason why the IME request was created.
+ * @param fromUser whether this request was created directly from user interaction.
*/
@NonNull
private ImeTracker.Token createStatsTokenForFocusedClient(boolean show,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+ @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
final int uid = mCurFocusedWindowClient != null
? mCurFocusedWindowClient.mUid
: -1;
@@ -6687,9 +6689,11 @@
: "uid(" + uid + ")";
if (show) {
- return ImeTracker.forLogging().onRequestShow(packageName, uid, origin, reason);
+ return ImeTracker.forLogging()
+ .onRequestShow(packageName, uid, origin, reason, fromUser);
} else {
- return ImeTracker.forLogging().onRequestHide(packageName, uid, origin, reason);
+ return ImeTracker.forLogging()
+ .onRequestHide(packageName, uid, origin, reason, fromUser);
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 775a361..25e4116 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4995,6 +4995,14 @@
}
break;
}
+ case KeyEvent.KEYCODE_STEM_PRIMARY: {
+ if (down && event.getRepeatCount() == 0 && (result & ACTION_PASS_TO_USER) == 0) {
+ // We've decided not to pass key to user at queueing stage. Make the gesture
+ // executable.
+ setDeferredKeyActionsExecutableAsync(keyCode, event.getDownTime());
+ }
+ break;
+ }
case KeyEvent.KEYCODE_VIDEO_APP_1:
case KeyEvent.KEYCODE_VIDEO_APP_2:
case KeyEvent.KEYCODE_VIDEO_APP_3:
diff --git a/services/core/java/com/android/server/uri/UriPermission.java b/services/core/java/com/android/server/uri/UriPermission.java
index f3eeab0..e406eb2 100644
--- a/services/core/java/com/android/server/uri/UriPermission.java
+++ b/services/core/java/com/android/server/uri/UriPermission.java
@@ -269,8 +269,9 @@
* Remove given read owner, updating {@Link #modeFlags} as needed.
*/
void removeReadOwner(UriPermissionOwner owner) {
- if (!mReadOwners.remove(owner)) {
+ if (mReadOwners == null || !mReadOwners.remove(owner)) {
Slog.wtf(TAG, "Unknown read owner " + owner + " in " + this);
+ return;
}
if (mReadOwners.size() == 0) {
mReadOwners = null;
@@ -294,8 +295,9 @@
* Remove given write owner, updating {@Link #modeFlags} as needed.
*/
void removeWriteOwner(UriPermissionOwner owner) {
- if (!mWriteOwners.remove(owner)) {
+ if (mWriteOwners == null || !mWriteOwners.remove(owner)) {
Slog.wtf(TAG, "Unknown write owner " + owner + " in " + this);
+ return;
}
if (mWriteOwners.size() == 0) {
mWriteOwners = null;
diff --git a/services/core/java/com/android/server/wm/ActivityAssistInfo.java b/services/core/java/com/android/server/wm/ActivityAssistInfo.java
index e1e7ee4..3b91780 100644
--- a/services/core/java/com/android/server/wm/ActivityAssistInfo.java
+++ b/services/core/java/com/android/server/wm/ActivityAssistInfo.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import android.content.ComponentName;
import android.os.IBinder;
/**
@@ -28,11 +29,13 @@
private final IBinder mActivityToken;
private final IBinder mAssistToken;
private final int mTaskId;
+ private final ComponentName mComponentName;
public ActivityAssistInfo(ActivityRecord activityRecord) {
this.mActivityToken = activityRecord.token;
this.mAssistToken = activityRecord.assistToken;
this.mTaskId = activityRecord.getTask().mTaskId;
+ this.mComponentName = activityRecord.mActivityComponent;
}
/** @hide */
@@ -49,4 +52,9 @@
public int getTaskId() {
return mTaskId;
}
+
+ /** @hide */
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
}
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 53c1960..6d3b8ac 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -119,3 +119,19 @@
"android.test.runner",
],
}
+
+android_ravenwood_test {
+ name: "FrameworksMockingServicesTestsRavenwood",
+ libs: [
+ "android.test.mock",
+ ],
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.test.rules",
+ "services.core",
+ ],
+ srcs: [
+ "src/com/android/server/am/BroadcastRecordTest.java",
+ ],
+ auto_gen_config: true,
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index 50d37ec..77e7a0a 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -34,6 +34,7 @@
import android.content.ComponentName;
import android.os.RemoteException;
import android.provider.Settings;
+import android.view.Display;
import org.junit.Test;
@@ -104,6 +105,27 @@
}
@Test
+ public void stemSingleKey_launchTargetActivity_whenScreenIsOff() {
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_SHORT_PRESS,
+ SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
+ mPhoneWindowManager.overrideStartActivity();
+ mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+ mPhoneWindowManager.assumeResolveActivityNotNull();
+ mPhoneWindowManager.overrideDisplayState(Display.STATE_OFF);
+ ComponentName targetComponent = ComponentName.unflattenFromString(TEST_TARGET_ACTIVITY);
+ mPhoneWindowManager.overrideStemPressTargetActivity(targetComponent);
+ mPhoneWindowManager.overrideKeyEventPolicyFlags(0);
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ mPhoneWindowManager.assertActivityTargetLaunched(targetComponent);
+ }
+
+ @Test
public void stemSingleKey_appHasOverridePermission_consumedByApp_notOpenAllApp() {
overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 2904c03..1a26c45 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -180,6 +180,8 @@
private boolean mIsTalkBackEnabled;
private boolean mIsTalkBackShortcutGestureEnabled;
+ private int mKeyEventPolicyFlags = FLAG_INTERACTIVE;
+
private class TestTalkbackShortcutController extends TalkbackShortcutController {
TestTalkbackShortcutController(Context context) {
super(context);
@@ -379,12 +381,12 @@
}
int interceptKeyBeforeQueueing(KeyEvent event) {
- return mPhoneWindowManager.interceptKeyBeforeQueueing(event, FLAG_INTERACTIVE);
+ return mPhoneWindowManager.interceptKeyBeforeQueueing(event, mKeyEventPolicyFlags);
}
long interceptKeyBeforeDispatching(KeyEvent event) {
return mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, event,
- FLAG_INTERACTIVE);
+ mKeyEventPolicyFlags);
}
void dispatchUnhandledKey(KeyEvent event) {
@@ -588,6 +590,10 @@
.when(mButtonOverridePermissionChecker).canAppOverrideSystemKey(any(), anyInt());
}
+ void overrideKeyEventPolicyFlags(int flags) {
+ mKeyEventPolicyFlags = flags;
+ }
+
/**
* Below functions will check the policy behavior could be invoked.
*/
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index d722f2f..5dd4feb 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -20,6 +20,7 @@
import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+import static android.service.voice.VoiceInteractionSession.KEY_FOREGROUND_ACTIVITIES;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
@@ -35,6 +36,7 @@
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
import android.app.UriGrantsManager;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
@@ -112,6 +114,7 @@
final Callback mCallback;
final int mCallingUid;
final Handler mHandler;
+ final IActivityTaskManager mActivityTaskManager;
final IActivityManager mAm;
final UriGrantsManagerInternal mUgmInternal;
final IWindowManager mIWindowManager;
@@ -224,6 +227,7 @@
mCallback = callback;
mCallingUid = callingUid;
mHandler = handler;
+ mActivityTaskManager = ActivityTaskManager.getService();
mAm = ActivityManager.getService();
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mIWindowManager = IWindowManager.Stub.asInterface(
@@ -300,11 +304,30 @@
for (int i = 0; i < topActivitiesCount; i++) {
topActivitiesToken.add(topActivities.get(i).getActivityToken());
}
+ boolean fetchDataAllowed =
+ (disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0;
+
+ // Ensure that the current activity supports assist data
+ boolean isAssistDataAllowed = false;
+ try {
+ isAssistDataAllowed = mActivityTaskManager.isAssistDataAllowed();
+ } catch (RemoteException e) {
+ // Should never happen
+ }
+
+ // TODO: Refactor to have all assist data allowed checks in one place.
+ if (fetchDataAllowed && isAssistDataAllowed) {
+ ArrayList<ComponentName> topComponents = new ArrayList<>(topActivitiesCount);
+ for (int i = 0; i < topActivitiesCount; i++) {
+ topComponents.add(topActivities.get(i).getComponentName());
+ }
+ mShowArgs.putParcelableArrayList(KEY_FOREGROUND_ACTIVITIES, topComponents);
+ }
mAssistDataRequester.requestAssistData(topActivitiesToken,
fetchData,
fetchScreenshot,
- (disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0,
+ fetchDataAllowed,
(disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0,
mCallingUid,
mSessionComponentName.getPackageName(),
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/ParcelFileDescriptor_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/ParcelFileDescriptor_host.java
new file mode 100644
index 0000000..0ebaac60
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/ParcelFileDescriptor_host.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 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.hoststubgen.nativesubstitution;
+
+import static android.os.ParcelFileDescriptor.MODE_APPEND;
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+import static android.os.ParcelFileDescriptor.MODE_WORLD_READABLE;
+import static android.os.ParcelFileDescriptor.MODE_WORLD_WRITEABLE;
+import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ParcelFileDescriptor_host {
+ /**
+ * Since we don't have a great way to keep an unmanaged {@code FileDescriptor} reference
+ * alive, we keep a strong reference to the {@code RandomAccessFile} we used to open it. This
+ * gives us a way to look up the original parent object when closing later.
+ */
+ @GuardedBy("sActive")
+ private static final Map<FileDescriptor, RandomAccessFile> sActive = new HashMap<>();
+
+ public static void native_setFdInt$ravenwood(FileDescriptor fd, int fdInt) {
+ try {
+ final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
+ "getJavaIOFileDescriptorAccess").invoke(null);
+ Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
+ "set", FileDescriptor.class, int.class).invoke(obj, fd, fdInt);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
+ + " perhaps JRE has changed?", e);
+ }
+ }
+
+ public static int native_getFdInt$ravenwood(FileDescriptor fd) {
+ try {
+ final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
+ "getJavaIOFileDescriptorAccess").invoke(null);
+ return (int) Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
+ "get", FileDescriptor.class).invoke(obj, fd);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
+ + " perhaps JRE has changed?", e);
+ }
+ }
+
+ public static FileDescriptor native_open$ravenwood(File file, int pfdMode) throws IOException {
+ if ((pfdMode & MODE_CREATE) != 0 && !file.exists()) {
+ throw new FileNotFoundException();
+ }
+
+ final String modeString;
+ if ((pfdMode & MODE_READ_WRITE) == MODE_READ_WRITE) {
+ modeString = "rw";
+ } else if ((pfdMode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) {
+ modeString = "rw";
+ } else if ((pfdMode & MODE_READ_ONLY) == MODE_READ_ONLY) {
+ modeString = "r";
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ final RandomAccessFile raf = new RandomAccessFile(file, modeString);
+
+ // Now that we have a real file on disk, match requested flags
+ if ((pfdMode & MODE_TRUNCATE) != 0) {
+ raf.setLength(0);
+ }
+ if ((pfdMode & MODE_APPEND) != 0) {
+ raf.seek(raf.length());
+ }
+ if ((pfdMode & MODE_WORLD_READABLE) != 0) {
+ file.setReadable(true, false);
+ }
+ if ((pfdMode & MODE_WORLD_WRITEABLE) != 0) {
+ file.setWritable(true, false);
+ }
+
+ final FileDescriptor fd = raf.getFD();
+ synchronized (sActive) {
+ sActive.put(fd, raf);
+ }
+ return fd;
+ }
+
+ public static void native_close$ravenwood(FileDescriptor fd) {
+ final RandomAccessFile raf;
+ synchronized (sActive) {
+ raf = sActive.remove(fd);
+ }
+ try {
+ if (raf != null) {
+ raf.close();
+ } else {
+ // Odd, we don't remember opening this ourselves, but let's release the
+ // underlying resource as requested
+ System.err.println("Closing unknown FileDescriptor: " + fd);
+ new FileOutputStream(fd).close();
+ }
+ } catch (IOException ignored) {
+ }
+ }
+}