diff options
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) |