summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt6
-rw-r--r--core/java/android/credentials/CredentialManager.java174
-rw-r--r--core/java/android/credentials/PrepareGetCredentialResponse.java7
-rw-r--r--core/java/com/android/internal/expresslog/Histogram.java13
-rw-r--r--core/jni/android_view_DisplayEventReceiver.cpp24
-rw-r--r--core/tests/coretests/src/android/credentials/CredentialManagerTest.java32
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt95
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java2
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt6
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java35
-rw-r--r--services/core/java/com/android/server/wm/Transition.java18
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java55
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt41
15 files changed, 456 insertions, 120 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 51c6f6a5eecc..e7d139dc4924 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -13655,9 +13655,9 @@ package android.credentials {
public final class CredentialManager {
method public void clearCredentialState(@NonNull android.credentials.ClearCredentialStateRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>);
- method public void createCredential(@NonNull android.credentials.CreateCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>);
- method public void getCredential(@NonNull android.credentials.GetCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
- method public void getCredential(@NonNull android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
+ method public void createCredential(@NonNull android.content.Context, @NonNull android.credentials.CreateCredentialRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>);
+ method public void getCredential(@NonNull android.content.Context, @NonNull android.credentials.GetCredentialRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
+ method public void getCredential(@NonNull android.content.Context, @NonNull android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
method public boolean isEnabledCredentialProviderService(@NonNull android.content.ComponentName);
method public void prepareGetCredential(@NonNull android.credentials.GetCredentialRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.PrepareGetCredentialResponse,android.credentials.GetCredentialException>);
method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest);
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 5579d2263d06..00ce17adfda6 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -25,11 +25,11 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
+import android.os.Binder;
import android.os.CancellationSignal;
import android.os.ICancellationSignal;
import android.os.OutcomeReceiver;
@@ -126,20 +126,21 @@ public final class CredentialManager {
* need additional permission {@link CREDENTIAL_MANAGER_SET_ORIGIN}
* to use this functionality
*
+ * @param context the context used to launch any UI needed; use an activity context to make sure
+ * the UI will be launched within the same task stack
* @param request the request specifying type(s) of credentials to get from the user
- * @param activity the activity used to launch any UI needed
* @param cancellationSignal an optional signal that allows for cancelling this call
* @param executor the callback will take place on this {@link Executor}
* @param callback the callback invoked when the request succeeds or fails
*/
public void getCredential(
+ @NonNull Context context,
@NonNull GetCredentialRequest request,
- @NonNull Activity activity,
@Nullable CancellationSignal cancellationSignal,
@CallbackExecutor @NonNull Executor executor,
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
requireNonNull(request, "request must not be null");
- requireNonNull(activity, "activity must not be null");
+ requireNonNull(context, "context must not be null");
requireNonNull(executor, "executor must not be null");
requireNonNull(callback, "callback must not be null");
@@ -153,7 +154,7 @@ public final class CredentialManager {
cancelRemote =
mService.executeGetCredential(
request,
- new GetCredentialTransport(activity, executor, callback),
+ new GetCredentialTransport(context, executor, callback),
mContext.getOpPackageName());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
@@ -175,21 +176,22 @@ public final class CredentialManager {
* request through the {@link #prepareGetCredential(
* GetCredentialRequest, CancellationSignal, Executor, OutcomeReceiver)} API.
*
+ * @param context the context used to launch any UI needed; use an activity context to make sure
+ * the UI will be launched within the same task stack
* @param pendingGetCredentialHandle the handle representing the pending operation to resume
- * @param activity the activity used to launch any UI needed
* @param cancellationSignal an optional signal that allows for cancelling this call
* @param executor the callback will take place on this {@link Executor}
* @param callback the callback invoked when the request succeeds or fails
*/
public void getCredential(
+ @NonNull Context context,
@NonNull PrepareGetCredentialResponse.PendingGetCredentialHandle
- pendingGetCredentialHandle,
- @NonNull Activity activity,
+ pendingGetCredentialHandle,
@Nullable CancellationSignal cancellationSignal,
@CallbackExecutor @NonNull Executor executor,
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
requireNonNull(pendingGetCredentialHandle, "pendingGetCredentialHandle must not be null");
- requireNonNull(activity, "activity must not be null");
+ requireNonNull(context, "context must not be null");
requireNonNull(executor, "executor must not be null");
requireNonNull(callback, "callback must not be null");
@@ -198,7 +200,7 @@ public final class CredentialManager {
return;
}
- pendingGetCredentialHandle.show(activity, cancellationSignal, executor, callback);
+ pendingGetCredentialHandle.show(context, cancellationSignal, executor, callback);
}
/**
@@ -207,9 +209,9 @@ public final class CredentialManager {
*
* <p>This API doesn't invoke any UI. It only performs the preparation work so that you can
* later launch the remaining get-credential operation (involves UIs) through the {@link
- * #getCredential(PrepareGetCredentialResponse.PendingGetCredentialHandle, Activity,
+ * #getCredential(PrepareGetCredentialResponse.PendingGetCredentialHandle, Context,
* CancellationSignal, Executor, OutcomeReceiver)} API which incurs less latency compared to
- * the {@link #getCredential(GetCredentialRequest, Activity, CancellationSignal, Executor,
+ * the {@link #getCredential(GetCredentialRequest, Context, CancellationSignal, Executor,
* OutcomeReceiver)} API that executes the whole operation in one call.
*
* @param request the request specifying type(s) of credentials to get from the user
@@ -262,21 +264,22 @@ public final class CredentialManager {
* need additional permission {@link CREDENTIAL_MANAGER_SET_ORIGIN}
* to use this functionality
*
+ * @param context the context used to launch any UI needed; use an activity context to make sure
+ * the UI will be launched within the same task stack
* @param request the request specifying type(s) of credentials to get from the user
- * @param activity the activity used to launch any UI needed
* @param cancellationSignal an optional signal that allows for cancelling this call
* @param executor the callback will take place on this {@link Executor}
* @param callback the callback invoked when the request succeeds or fails
*/
public void createCredential(
+ @NonNull Context context,
@NonNull CreateCredentialRequest request,
- @NonNull Activity activity,
@Nullable CancellationSignal cancellationSignal,
@CallbackExecutor @NonNull Executor executor,
@NonNull
OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback) {
requireNonNull(request, "request must not be null");
- requireNonNull(activity, "activity must not be null");
+ requireNonNull(context, "context must not be null");
requireNonNull(executor, "executor must not be null");
requireNonNull(callback, "callback must not be null");
@@ -290,7 +293,7 @@ public final class CredentialManager {
cancelRemote =
mService.executeCreateCredential(
request,
- new CreateCredentialTransport(activity, executor, callback),
+ new CreateCredentialTransport(context, executor, callback),
mContext.getOpPackageName());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
@@ -547,14 +550,24 @@ public final class CredentialManager {
@Override
public void onResponse(PrepareGetCredentialResponseInternal response) {
- mExecutor.execute(() -> mCallback.onResult(
- new PrepareGetCredentialResponse(response, mGetCredentialTransport)));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onResult(
+ new PrepareGetCredentialResponse(response, mGetCredentialTransport)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
public void onError(String errorType, String message) {
- mExecutor.execute(
- () -> mCallback.onError(new GetCredentialException(errorType, message)));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallback.onError(new GetCredentialException(errorType, message)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -587,7 +600,12 @@ public final class CredentialManager {
@Override
public void onResponse(GetCredentialResponse response) {
if (mCallback != null) {
- mCallback.onResponse(response);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mCallback.onResponse(response);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
} else {
Log.d(TAG, "Unexpected onResponse call before the show invocation");
}
@@ -596,7 +614,12 @@ public final class CredentialManager {
@Override
public void onError(String errorType, String message) {
if (mCallback != null) {
- mCallback.onError(errorType, message);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mCallback.onError(errorType, message);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
} else {
Log.d(TAG, "Unexpected onError call before the show invocation");
}
@@ -606,15 +629,15 @@ public final class CredentialManager {
private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
// TODO: listen for cancellation to release callback.
- private final Activity mActivity;
+ private final Context mContext;
private final Executor mExecutor;
private final OutcomeReceiver<GetCredentialResponse, GetCredentialException> mCallback;
private GetCredentialTransport(
- Activity activity,
+ Context context,
Executor executor,
OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
- mActivity = activity;
+ mContext = context;
mExecutor = executor;
mCallback = callback;
}
@@ -622,42 +645,57 @@ public final class CredentialManager {
@Override
public void onPendingIntent(PendingIntent pendingIntent) {
try {
- mActivity.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
+ mContext.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(
TAG,
"startIntentSender() failed for intent:" + pendingIntent.getIntentSender(),
e);
- mExecutor.execute(() -> mCallback.onError(
- new GetCredentialException(GetCredentialException.TYPE_UNKNOWN)));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onError(
+ new GetCredentialException(GetCredentialException.TYPE_UNKNOWN)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@Override
public void onResponse(GetCredentialResponse response) {
- mExecutor.execute(() -> mCallback.onResult(response));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onResult(response));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
public void onError(String errorType, String message) {
- mExecutor.execute(
- () -> mCallback.onError(new GetCredentialException(errorType, message)));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallback.onError(new GetCredentialException(errorType, message)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
private static class CreateCredentialTransport extends ICreateCredentialCallback.Stub {
// TODO: listen for cancellation to release callback.
- private final Activity mActivity;
+ private final Context mContext;
private final Executor mExecutor;
private final OutcomeReceiver<CreateCredentialResponse, CreateCredentialException>
mCallback;
private CreateCredentialTransport(
- Activity activity,
+ Context context,
Executor executor,
OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback) {
- mActivity = activity;
+ mContext = context;
mExecutor = executor;
mCallback = callback;
}
@@ -665,26 +703,41 @@ public final class CredentialManager {
@Override
public void onPendingIntent(PendingIntent pendingIntent) {
try {
- mActivity.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
+ mContext.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(
TAG,
"startIntentSender() failed for intent:" + pendingIntent.getIntentSender(),
e);
- mExecutor.execute(() -> mCallback.onError(
- new CreateCredentialException(CreateCredentialException.TYPE_UNKNOWN)));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onError(
+ new CreateCredentialException(CreateCredentialException.TYPE_UNKNOWN)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@Override
public void onResponse(CreateCredentialResponse response) {
- mExecutor.execute(() -> mCallback.onResult(response));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onResult(response));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
public void onError(String errorType, String message) {
- mExecutor.execute(
- () -> mCallback.onError(new CreateCredentialException(errorType, message)));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallback.onError(new CreateCredentialException(errorType, message)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -702,13 +755,24 @@ public final class CredentialManager {
@Override
public void onSuccess() {
- mCallback.onResult(null);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mCallback.onResult(null);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
public void onError(String errorType, String message) {
- mExecutor.execute(
- () -> mCallback.onError(new ClearCredentialStateException(errorType, message)));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallback.onError(
+ new ClearCredentialStateException(errorType, message)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -725,18 +789,34 @@ public final class CredentialManager {
}
public void onResponse(Void result) {
- mExecutor.execute(() -> mCallback.onResult(result));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onResult(result));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
public void onResponse() {
- mExecutor.execute(() -> mCallback.onResult(null));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onResult(null));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
public void onError(String errorType, String message) {
- mExecutor.execute(
- () -> mCallback.onError(new SetEnabledProvidersException(errorType, message)));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallback.onError(
+ new SetEnabledProvidersException(errorType, message)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
}
diff --git a/core/java/android/credentials/PrepareGetCredentialResponse.java b/core/java/android/credentials/PrepareGetCredentialResponse.java
index 81e906859cb8..056b18a51b6d 100644
--- a/core/java/android/credentials/PrepareGetCredentialResponse.java
+++ b/core/java/android/credentials/PrepareGetCredentialResponse.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.Activity;
import android.app.PendingIntent;
+import android.content.Context;
import android.content.IntentSender;
import android.os.CancellationSignal;
import android.os.OutcomeReceiver;
@@ -67,7 +68,7 @@ public final class PrepareGetCredentialResponse {
}
/** @hide */
- void show(@NonNull Activity activity, @Nullable CancellationSignal cancellationSignal,
+ void show(@NonNull Context context, @Nullable CancellationSignal cancellationSignal,
@CallbackExecutor @NonNull Executor executor,
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
if (mPendingIntent == null) {
@@ -80,7 +81,7 @@ public final class PrepareGetCredentialResponse {
@Override
public void onPendingIntent(PendingIntent pendingIntent) {
try {
- activity.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
+ context.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "startIntentSender() failed for intent for show()", e);
executor.execute(() -> callback.onError(
@@ -101,7 +102,7 @@ public final class PrepareGetCredentialResponse {
});
try {
- activity.startIntentSender(mPendingIntent.getIntentSender(), null, 0, 0, 0);
+ context.startIntentSender(mPendingIntent.getIntentSender(), null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "startIntentSender() failed for intent for show()", e);
executor.execute(() -> callback.onError(
diff --git a/core/java/com/android/internal/expresslog/Histogram.java b/core/java/com/android/internal/expresslog/Histogram.java
index 65fbb03bf967..2fe784a5a855 100644
--- a/core/java/com/android/internal/expresslog/Histogram.java
+++ b/core/java/com/android/internal/expresslog/Histogram.java
@@ -54,6 +54,19 @@ public final class Histogram {
/*count*/ 1, binIndex);
}
+ /**
+ * Logs increment sample count for automatically calculated bin
+ *
+ * @param uid used as a dimension for the count metric
+ * @param sample value
+ * @hide
+ */
+ public void logSampleWithUid(int uid, float sample) {
+ final int binIndex = mBinOptions.getBinForSample(sample);
+ FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED,
+ mMetricIdHash, /*count*/ 1, binIndex, uid);
+ }
+
/** Used by Histogram to map data sample to corresponding bin */
public interface BinOptions {
/**
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 0c3ff6c28ea7..410b44161cf6 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -18,19 +18,17 @@
//#define LOG_NDEBUG 0
-#include <nativehelper/JNIHelp.h>
-
-#include <inttypes.h>
-
#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
#include <gui/DisplayEventDispatcher.h>
+#include <inttypes.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
#include <utils/Log.h>
#include <utils/Looper.h>
#include <utils/threads.h>
-#include "android_os_MessageQueue.h"
-
-#include <nativehelper/ScopedLocalRef.h>
+#include "android_os_MessageQueue.h"
#include "core_jni_helpers.h"
namespace android {
@@ -133,6 +131,12 @@ static jobject createJavaVsyncEventData(JNIEnv* env, VsyncEventData vsyncEventDa
gDisplayEventReceiverClassInfo
.frameTimelineClassInfo.clazz,
/*initial element*/ NULL));
+ if (!frameTimelineObjs.get() || env->ExceptionCheck()) {
+ ALOGW("%s: Failed to create FrameTimeline array", __func__);
+ LOGW_EX(env);
+ env->ExceptionClear();
+ return NULL;
+ }
for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
VsyncEventData::FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i];
ScopedLocalRef<jobject>
@@ -144,6 +148,12 @@ static jobject createJavaVsyncEventData(JNIEnv* env, VsyncEventData vsyncEventDa
frameTimeline.vsyncId,
frameTimeline.expectedPresentationTime,
frameTimeline.deadlineTimestamp));
+ if (!frameTimelineObj.get() || env->ExceptionCheck()) {
+ ALOGW("%s: Failed to create FrameTimeline object", __func__);
+ LOGW_EX(env);
+ env->ExceptionClear();
+ return NULL;
+ }
env->SetObjectArrayElement(frameTimelineObjs.get(), i, frameTimelineObj.get());
}
return env->NewObject(gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
diff --git a/core/tests/coretests/src/android/credentials/CredentialManagerTest.java b/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
index c7e026123487..b0d5240c61ba 100644
--- a/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
+++ b/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
@@ -144,7 +144,7 @@ public class CredentialManagerTest {
public void testGetCredential_nullRequest() {
GetCredentialRequest nullRequest = null;
assertThrows(NullPointerException.class,
- () -> mCredentialManager.getCredential(nullRequest, mMockActivity, null, mExecutor,
+ () -> mCredentialManager.getCredential(mMockActivity, nullRequest, null, mExecutor,
result -> {
}));
}
@@ -152,7 +152,7 @@ public class CredentialManagerTest {
@Test
public void testGetCredential_nullActivity() {
assertThrows(NullPointerException.class,
- () -> mCredentialManager.getCredential(mGetRequest, null, null, mExecutor,
+ () -> mCredentialManager.getCredential(null, mGetRequest, null, mExecutor,
result -> {
}));
}
@@ -160,7 +160,7 @@ public class CredentialManagerTest {
@Test
public void testGetCredential_nullExecutor() {
assertThrows(NullPointerException.class,
- () -> mCredentialManager.getCredential(mGetRequest, mMockActivity, null, null,
+ () -> mCredentialManager.getCredential(mMockActivity, mGetRequest, null, null,
result -> {
}));
}
@@ -168,7 +168,7 @@ public class CredentialManagerTest {
@Test
public void testGetCredential_nullCallback() {
assertThrows(NullPointerException.class,
- () -> mCredentialManager.getCredential(mGetRequest, mMockActivity, null, null,
+ () -> mCredentialManager.getCredential(mMockActivity, mGetRequest, null, null,
null));
}
@@ -184,7 +184,7 @@ public class CredentialManagerTest {
when(mMockCredentialManagerService.executeGetCredential(any(), callbackCaptor.capture(),
any())).thenReturn(mock(ICancellationSignal.class));
- mCredentialManager.getCredential(mGetRequest, mMockActivity, null, mExecutor, callback);
+ mCredentialManager.getCredential(mMockActivity, mGetRequest, null, mExecutor, callback);
verify(mMockCredentialManagerService).executeGetCredential(any(), any(), eq(mPackageName));
callbackCaptor.getValue().onError(GetCredentialException.TYPE_NO_CREDENTIAL,
@@ -200,7 +200,7 @@ public class CredentialManagerTest {
final CancellationSignal cancellation = new CancellationSignal();
cancellation.cancel();
- mCredentialManager.getCredential(mGetRequest, mMockActivity, cancellation, mExecutor,
+ mCredentialManager.getCredential(mMockActivity, mGetRequest, cancellation, mExecutor,
result -> {
});
@@ -218,7 +218,7 @@ public class CredentialManagerTest {
when(mMockCredentialManagerService.executeGetCredential(any(), any(), any())).thenReturn(
serviceSignal);
- mCredentialManager.getCredential(mGetRequest, mMockActivity, cancellation, mExecutor,
+ mCredentialManager.getCredential(mMockActivity, mGetRequest, cancellation, mExecutor,
callback);
verify(mMockCredentialManagerService).executeGetCredential(any(), any(), eq(mPackageName));
@@ -241,7 +241,7 @@ public class CredentialManagerTest {
when(mMockCredentialManagerService.executeGetCredential(any(), callbackCaptor.capture(),
any())).thenReturn(mock(ICancellationSignal.class));
- mCredentialManager.getCredential(mGetRequest, mMockActivity, null, mExecutor, callback);
+ mCredentialManager.getCredential(mMockActivity, mGetRequest, null, mExecutor, callback);
verify(mMockCredentialManagerService).executeGetCredential(any(), any(), eq(mPackageName));
callbackCaptor.getValue().onResponse(new GetCredentialResponse(cred));
@@ -253,7 +253,7 @@ public class CredentialManagerTest {
@Test
public void testCreateCredential_nullRequest() {
assertThrows(NullPointerException.class,
- () -> mCredentialManager.createCredential(null, mMockActivity, null, mExecutor,
+ () -> mCredentialManager.createCredential(mMockActivity, null, null, mExecutor,
result -> {
}));
}
@@ -261,7 +261,7 @@ public class CredentialManagerTest {
@Test
public void testCreateCredential_nullActivity() {
assertThrows(NullPointerException.class,
- () -> mCredentialManager.createCredential(mCreateRequest, null, null, mExecutor,
+ () -> mCredentialManager.createCredential(null, mCreateRequest, null, mExecutor,
result -> {
}));
}
@@ -269,7 +269,7 @@ public class CredentialManagerTest {
@Test
public void testCreateCredential_nullExecutor() {
assertThrows(NullPointerException.class,
- () -> mCredentialManager.createCredential(mCreateRequest, mMockActivity, null, null,
+ () -> mCredentialManager.createCredential(mMockActivity, mCreateRequest, null, null,
result -> {
}));
}
@@ -277,7 +277,7 @@ public class CredentialManagerTest {
@Test
public void testCreateCredential_nullCallback() {
assertThrows(NullPointerException.class,
- () -> mCredentialManager.createCredential(mCreateRequest, mMockActivity, null,
+ () -> mCredentialManager.createCredential(mMockActivity, mCreateRequest, null,
mExecutor, null));
}
@@ -286,7 +286,7 @@ public class CredentialManagerTest {
final CancellationSignal cancellation = new CancellationSignal();
cancellation.cancel();
- mCredentialManager.createCredential(mCreateRequest, mMockActivity, cancellation, mExecutor,
+ mCredentialManager.createCredential(mMockActivity, mCreateRequest, cancellation, mExecutor,
result -> {
});
@@ -304,7 +304,7 @@ public class CredentialManagerTest {
when(mMockCredentialManagerService.executeCreateCredential(any(), any(), any())).thenReturn(
serviceSignal);
- mCredentialManager.createCredential(mCreateRequest, mMockActivity, cancellation, mExecutor,
+ mCredentialManager.createCredential(mMockActivity, mCreateRequest, cancellation, mExecutor,
callback);
verify(mMockCredentialManagerService).executeCreateCredential(any(), any(),
@@ -326,7 +326,7 @@ public class CredentialManagerTest {
when(mMockCredentialManagerService.executeCreateCredential(any(), callbackCaptor.capture(),
any())).thenReturn(mock(ICancellationSignal.class));
- mCredentialManager.createCredential(mCreateRequest, mMockActivity, null, mExecutor,
+ mCredentialManager.createCredential(mMockActivity, mCreateRequest, null, mExecutor,
callback);
verify(mMockCredentialManagerService).executeCreateCredential(any(), any(),
eq(mPackageName));
@@ -353,7 +353,7 @@ public class CredentialManagerTest {
when(mMockCredentialManagerService.executeCreateCredential(any(), callbackCaptor.capture(),
any())).thenReturn(mock(ICancellationSignal.class));
- mCredentialManager.createCredential(mCreateRequest, mMockActivity, null, mExecutor,
+ mCredentialManager.createCredential(mMockActivity, mCreateRequest, null, mExecutor,
callback);
verify(mMockCredentialManagerService).executeCreateCredential(any(), any(),
eq(mPackageName));
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 51f99ec637da..22462eb11a8f 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -38,8 +38,11 @@ import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
import android.net.Uri;
import android.os.Build;
+import android.os.SystemProperties;
import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
@@ -928,6 +931,8 @@ public final class ImageDecoder implements AutoCloseable {
case "image/x-pentax-pef":
case "image/x-samsung-srw":
return true;
+ case "image/avif":
+ return isP010SupportedForAV1();
default:
return false;
}
@@ -2063,6 +2068,49 @@ public final class ImageDecoder implements AutoCloseable {
return decodeBitmapImpl(src, null);
}
+ private static boolean sIsP010SupportedForAV1 = false;
+ private static boolean sIsP010SupportedForAV1Initialized = false;
+
+ /**
+ * Checks if the device supports decoding 10-bit AV1.
+ */
+ private static boolean isP010SupportedForAV1() {
+ if (sIsP010SupportedForAV1Initialized) {
+ return sIsP010SupportedForAV1;
+ }
+
+ sIsP010SupportedForAV1Initialized = true;
+
+ if (hasHardwareDecoder("video/av01")) {
+ sIsP010SupportedForAV1 = true;
+ return true;
+ }
+
+ sIsP010SupportedForAV1 = SystemProperties.getInt("ro.product.first_api_level", 0) >=
+ Build.VERSION_CODES.S;
+ return sIsP010SupportedForAV1;
+ }
+
+ /**
+ * Checks if the device has hardware decoder for the target mime type.
+ */
+ private static boolean hasHardwareDecoder(String mime) {
+ final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ for (MediaCodecInfo info : sMCL.getCodecInfos()) {
+ if (info.isEncoder() == false && info.isHardwareAccelerated()) {
+ try {
+ if (info.getCapabilitiesForType(mime) != null) {
+ return true;
+ }
+ } catch (IllegalArgumentException e) {
+ // mime is not supported
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Private method called by JNI.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 349ff36ce8ce..6f993aebdc65 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -384,8 +384,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
private void onMove() {
- if (!mBackGestureStarted || mBackNavigationInfo == null || !mEnableAnimations.get()
- || mActiveCallback == null) {
+ if (!mBackGestureStarted || mBackNavigationInfo == null || mActiveCallback == null) {
return;
}
@@ -424,9 +423,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return;
}
try {
- if (mEnableAnimations.get()) {
- callback.onBackStarted(backEvent);
- }
+ callback.onBackStarted(backEvent);
} catch (RemoteException e) {
Log.e(TAG, "dispatchOnBackStarted error: ", e);
}
@@ -448,9 +445,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return;
}
try {
- if (mEnableAnimations.get()) {
- callback.onBackCancelled();
- }
+ callback.onBackCancelled();
} catch (RemoteException e) {
Log.e(TAG, "dispatchOnBackCancelled error: ", e);
}
@@ -462,19 +457,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return;
}
try {
- if (mEnableAnimations.get()) {
- callback.onBackProgressed(backEvent);
- }
+ callback.onBackProgressed(backEvent);
} catch (RemoteException e) {
Log.e(TAG, "dispatchOnBackProgressed error: ", e);
}
}
- private boolean shouldDispatchAnimation(IOnBackInvokedCallback callback) {
- // TODO(b/258698745): Only dispatch to animation callbacks.
- return mEnableAnimations.get();
- }
-
/**
* Sets to true when the back gesture has passed the triggering threshold, false otherwise.
*/
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
new file mode 100644
index 000000000000..083cfd294f96
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.flicker.pip
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test the dragging of a PIP window.
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipDragTest(flicker: FlickerTest) : PipTransition(flicker) {
+ private var isDraggedLeft: Boolean = true
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ val stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")
+
+ setup {
+ tapl.setEnableRotation(true)
+ // Launch the PIP activity and wait for it to enter PiP mode
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
+
+ // determine the direction of dragging to test for
+ isDraggedLeft = pipApp.isCloserToRightEdge(wmHelper)
+ }
+ teardown {
+ // release the primary pointer after dragging without release
+ pipApp.releasePipAfterDragging()
+
+ pipApp.exit(wmHelper)
+ tapl.setEnableRotation(false)
+ }
+ transitions {
+ pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun pipLayerMovesAwayFromEdge() {
+ flicker.assertLayers {
+ val pipLayerList = layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+ pipLayerList.zipWithNext { previous, current ->
+ if (isDraggedLeft) {
+ previous.visibleRegion.isToTheRight(current.visibleRegion.region)
+ } else {
+ current.visibleRegion.isToTheRight(previous.visibleRegion.region)
+ }
+ }
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 169b9bd4dea7..806bffebd4cb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -253,8 +253,6 @@ public class BackAnimationControllerTest extends ShellTestCase {
triggerBackGesture();
- verify(mAppCallback, never()).onBackStarted(any());
- verify(mAppCallback, never()).onBackProgressed(backEventCaptor.capture());
verify(mAppCallback, times(1)).onBackInvoked();
verify(mAnimatorCallback, never()).onBackStarted(any());
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 5b6a83c24e3d..0080517a722b 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -137,6 +137,12 @@ interface ClockAnimations {
fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {}
/**
+ * Runs when swiping clock picker, swipingFraction: 1.0 -> clock is scaled up in the preview,
+ * 0.0 -> clock is scaled down in the shade; previewRatio is previewSize / screenSize
+ */
+ fun onPickerCarouselSwiping(swipingFraction: Float, previewRatio: Float) {}
+
+ /**
* Whether this clock has a custom position update animation. If true, the keyguard will call
* `onPositionUpdated` to notify the clock of a position update animation. If false, a default
* animation will be used (e.g. a simple translation).
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index fb79c0677a2f..6eb2e05a8e3b 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -675,14 +675,29 @@ class BackNavigationController {
private static final int TASK_SWITCH = 1;
private static final int ACTIVITY_SWITCH = 2;
+ private static boolean isActivitySwitch(WindowContainer close, WindowContainer open) {
+ if (close.asActivityRecord() == null || open.asActivityRecord() == null
+ || (close.asActivityRecord().getTask()
+ != open.asActivityRecord().getTask())) {
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean isTaskSwitch(WindowContainer close, WindowContainer open) {
+ if (close.asTask() == null || open.asTask() == null
+ || (close.asTask() == open.asTask())) {
+ return false;
+ }
+ return true;
+ }
+
private void initiate(WindowContainer close, WindowContainer open) {
WindowContainer closeTarget;
- if (close.asActivityRecord() != null && open.asActivityRecord() != null
- && (close.asActivityRecord().getTask() == open.asActivityRecord().getTask())) {
+ if (isActivitySwitch(close, open)) {
mSwitchType = ACTIVITY_SWITCH;
closeTarget = close.asActivityRecord();
- } else if (close.asTask() != null && open.asTask() != null
- && close.asTask() != open.asTask()) {
+ } else if (isTaskSwitch(close, open)) {
mSwitchType = TASK_SWITCH;
closeTarget = close.asTask().getTopNonFinishingActivity();
} else {
@@ -757,15 +772,15 @@ class BackNavigationController {
}
boolean isTarget(WindowContainer wc, boolean open) {
- if (open) {
- return wc == mOpenAdaptor.mTarget || mOpenAdaptor.mTarget.hasChild(wc);
+ if (!mComposed) {
+ return false;
}
-
+ final WindowContainer target = open ? mOpenAdaptor.mTarget : mCloseAdaptor.mTarget;
if (mSwitchType == TASK_SWITCH) {
- return wc == mCloseAdaptor.mTarget
- || (wc.asTask() != null && wc.hasChild(mCloseAdaptor.mTarget));
+ return wc == target
+ || (wc.asTask() != null && wc.hasChild(target));
} else if (mSwitchType == ACTIVITY_SWITCH) {
- return wc == mCloseAdaptor.mTarget;
+ return wc == target || (wc.asTaskFragment() != null && wc.hasChild(target));
}
return false;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 2cfd2af89c80..8fa7592e6da0 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2363,6 +2363,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (isTranslucent(wc)) {
flags |= FLAG_TRANSLUCENT;
}
+ if (wc.mWmService.mAtmService.mBackNavigationController.isMonitorTransitionTarget(wc)) {
+ flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+ }
final Task task = wc.asTask();
if (task != null) {
final ActivityRecord topActivity = task.getTopNonFinishingActivity();
@@ -2371,19 +2374,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
&& topActivity.mStartingData.hasImeSurface()) {
flags |= FLAG_WILL_IME_SHOWN;
}
- if (topActivity.mAtmService.mBackNavigationController
- .isMonitorTransitionTarget(topActivity)) {
- flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
- }
- if (topActivity != null && topActivity.mLaunchTaskBehind) {
+ if (topActivity.mLaunchTaskBehind) {
Slog.e(TAG, "Unexpected launch-task-behind operation in shell transition");
flags |= FLAG_TASK_LAUNCHING_BEHIND;
}
- } else {
- if (task.mAtmService.mBackNavigationController
- .isMonitorTransitionTarget(task)) {
- flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
- }
}
if (task.voiceSession != null) {
flags |= FLAG_IS_VOICE_INTERACTION;
@@ -2397,10 +2391,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
flags |= FLAG_IS_VOICE_INTERACTION;
}
flags |= record.mTransitionChangeFlags;
- if (record.mAtmService.mBackNavigationController
- .isMonitorTransitionTarget(record)) {
- flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
- }
}
final TaskFragment taskFragment = wc.asTaskFragment();
if (taskFragment != null && task == null) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
index 858cd7672fe3..a8f1b3de564e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
@@ -26,6 +26,8 @@ import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
+import androidx.annotation.Nullable;
+
/**
* Injects gestures given an {@link Instrumentation} object.
*/
@@ -36,6 +38,13 @@ public class GestureHelper {
private final UiAutomation mUiAutomation;
/**
+ * Primary pointer should be cached here for separate release
+ */
+ @Nullable private PointerProperties mPrimaryPtrProp;
+ @Nullable private PointerCoords mPrimaryPtrCoord;
+ private long mPrimaryPtrDownTime;
+
+ /**
* A pair of floating point values.
*/
public static class Tuple {
@@ -53,6 +62,52 @@ public class GestureHelper {
}
/**
+ * Injects a series of {@link MotionEvent}s to simulate a drag gesture without pointer release.
+ *
+ * Simulates a drag gesture without releasing the primary pointer. The primary pointer info
+ * will be cached for potential release later on by {@code releasePrimaryPointer()}
+ *
+ * @param startPoint initial coordinates of the primary pointer
+ * @param endPoint final coordinates of the primary pointer
+ * @param steps number of steps to take to animate dragging
+ * @return true if gesture is injected successfully
+ */
+ public boolean dragWithoutRelease(@NonNull Tuple startPoint,
+ @NonNull Tuple endPoint, int steps) {
+ PointerProperties ptrProp = getPointerProp(0, MotionEvent.TOOL_TYPE_FINGER);
+ PointerCoords ptrCoord = getPointerCoord(startPoint.x, startPoint.y, 1, 1);
+
+ PointerProperties[] ptrProps = new PointerProperties[] { ptrProp };
+ PointerCoords[] ptrCoords = new PointerCoords[] { ptrCoord };
+
+ long downTime = SystemClock.uptimeMillis();
+
+ if (!primaryPointerDown(ptrProp, ptrCoord, downTime)) {
+ return false;
+ }
+
+ // cache the primary pointer info for later potential release
+ mPrimaryPtrProp = ptrProp;
+ mPrimaryPtrCoord = ptrCoord;
+ mPrimaryPtrDownTime = downTime;
+
+ return movePointers(ptrProps, ptrCoords, new Tuple[] { endPoint }, downTime, steps);
+ }
+
+ /**
+ * Release primary pointer if previous gesture has cached the primary pointer info.
+ *
+ * @return true if the release was injected successfully
+ */
+ public boolean releasePrimaryPointer() {
+ if (mPrimaryPtrProp != null && mPrimaryPtrCoord != null) {
+ return primaryPointerUp(mPrimaryPtrProp, mPrimaryPtrCoord, mPrimaryPtrDownTime);
+ }
+
+ return false;
+ }
+
+ /**
* Injects a series of {@link MotionEvent} objects to simulate a pinch gesture.
*
* @param startPoint1 initial coordinates of the first pointer
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index de57d06a5619..e497ae4779a7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -58,6 +58,43 @@ open class PipAppHelper(instrumentation: Instrumentation) :
}
/**
+ * Drags the PIP window to the provided final coordinates without releasing the pointer.
+ */
+ fun dragPipWindowAwayFromEdgeWithoutRelease(
+ wmHelper: WindowManagerStateHelper,
+ steps: Int
+ ) {
+ val initWindowRect = getWindowRect(wmHelper).clone()
+
+ // initial pointer at the center of the window
+ val initialCoord = GestureHelper.Tuple(initWindowRect.centerX().toFloat(),
+ initWindowRect.centerY().toFloat())
+
+ // the offset to the right (or left) of the window center to drag the window to
+ val offset = 50
+
+ // the actual final x coordinate with the offset included;
+ // if the pip window is closer to the right edge of the display the offset is negative
+ // otherwise the offset is positive
+ val endX = initWindowRect.centerX() +
+ offset * (if (isCloserToRightEdge(wmHelper)) -1 else 1)
+ val finalCoord = GestureHelper.Tuple(endX.toFloat(), initWindowRect.centerY().toFloat())
+
+ // drag to the final coordinate
+ gestureHelper.dragWithoutRelease(initialCoord, finalCoord, steps)
+ }
+
+ /**
+ * Releases the primary pointer.
+ *
+ * Injects the release of the primary pointer if the primary pointer info was cached after
+ * another gesture was injected without pointer release.
+ */
+ fun releasePipAfterDragging() {
+ gestureHelper.releasePrimaryPointer()
+ }
+
+ /**
* Drags the PIP window away from the screen edge while not crossing the display center.
*
* @throws IllegalStateException if default display bounds are not available
@@ -72,10 +109,10 @@ open class PipAppHelper(instrumentation: Instrumentation) :
val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
?: throw IllegalStateException("Default display is null")
- // the offset to the right of the display center to drag the window to
+ // the offset to the right (or left) of the display center to drag the window to
val offset = 20
- // the actual final x coordinate with the offset included
+ // the actual final x coordinate with the offset included;
// if the pip window is closer to the right edge of the display the offset is positive
// otherwise the offset is negative
val endX = displayRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) 1 else -1)