From 357411faa47f54fb717ef779fd1b12addccb832e Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Tue, 19 May 2015 11:32:08 -0700 Subject: [PATCH 1/5] Handle error states when refreshing accessibility node Bug: 21281869 Change-Id: Idce90f5e23b102b9c66f288906eedb5b6a5b098d --- core/java/android/view/ViewRootImpl.java | 34 +++++++++++++++++------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 41f906aeae16e..e59560fa1769d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -66,6 +66,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; import android.view.accessibility.AccessibilityManager.HighTextContrastChangeListener; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; @@ -6354,16 +6355,18 @@ public final class ViewRootImpl implements ViewParent, * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} */ private void handleWindowContentChangedEvent(AccessibilityEvent event) { - // No virtual view focused, nothing to do here. - if (mAccessibilityFocusedHost == null || mAccessibilityFocusedVirtualView == null) { + final View focusedHost = mAccessibilityFocusedHost; + if (focusedHost == null || mAccessibilityFocusedVirtualView == null) { + // No virtual view focused, nothing to do here. return; } - // If we have a node but no provider, abort. - final AccessibilityNodeProvider provider = - mAccessibilityFocusedHost.getAccessibilityNodeProvider(); + final AccessibilityNodeProvider provider = focusedHost.getAccessibilityNodeProvider(); if (provider == null) { - // TODO: Should we clear the focused virtual view? + // Error state: virtual view with no provider. Clear focus. + mAccessibilityFocusedHost = null; + mAccessibilityFocusedVirtualView = null; + focusedHost.clearAccessibilityFocusNoCallbacks(); return; } @@ -6410,10 +6413,23 @@ public final class ViewRootImpl implements ViewParent, final Rect oldBounds = mTempRect; mAccessibilityFocusedVirtualView.getBoundsInScreen(oldBounds); mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(focusedChildId); - final Rect newBounds = mAccessibilityFocusedVirtualView.getBoundsInScreen(); - if (!oldBounds.equals(newBounds)) { - oldBounds.union(newBounds); + if (mAccessibilityFocusedVirtualView == null) { + // Error state: The node no longer exists. Clear focus. + mAccessibilityFocusedHost = null; + focusedHost.clearAccessibilityFocusNoCallbacks(); + + // This will probably fail, but try to keep the provider's internal + // state consistent by clearing focus. + provider.performAction(focusedChildId, + AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(), null); invalidateRectOnScreen(oldBounds); + } else { + // The node was refreshed, invalidate bounds if necessary. + final Rect newBounds = mAccessibilityFocusedVirtualView.getBoundsInScreen(); + if (!oldBounds.equals(newBounds)) { + oldBounds.union(newBounds); + invalidateRectOnScreen(oldBounds); + } } } From 91b8a5a71233d2bcf0a09afce3baca1f492ad723 Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Fri, 22 May 2015 17:18:54 -0700 Subject: [PATCH 2/5] Unflip TextureView getBitmap readback bug:20165725 Change-Id: I09035f43be140145c54221d6865206ce486994c9 --- libs/hwui/LayerRenderer.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 223bdf06dca95..d9b40aebe7757 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -439,13 +439,6 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* renderer.translate(0.0f, bitmap->height()); renderer.scale(1.0f, -1.0f); - mat4 texTransform(layer->getTexTransform()); - - mat4 invert; - invert.translate(0.0f, 1.0f); - invert.scale(1.0f, -1.0f, 1.0f); - layer->getTexTransform().multiply(invert); - if ((error = glGetError()) != GL_NO_ERROR) goto error; { @@ -459,7 +452,6 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* if ((error = glGetError()) != GL_NO_ERROR) goto error; } - layer->getTexTransform().load(texTransform); status = true; } From b25bc454b564f4fc591456305a465b95a40b9cf3 Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 27 May 2015 16:29:17 -0700 Subject: [PATCH 3/5] Fix reconfigure & setPremult alpha handling Bug: 20948129 Change-Id: Ifba35e5d87772a304fd3655e4a2363b293a6d8ac --- core/jni/android/graphics/Bitmap.cpp | 31 +++++++++++++++++++++------- core/jni/android/graphics/Bitmap.h | 1 + 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 04b9a95bf3cf2..832f92f07a052 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -46,8 +46,8 @@ public: SkSafeUnref(mColorTable); } - void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) { - if (kIndex_8_SkColorType != info.colorType()) { + void reconfigure(const SkImageInfo& newInfo, size_t rowBytes, SkColorTable* ctable) { + if (kIndex_8_SkColorType != newInfo.colorType()) { ctable = nullptr; } mRowBytes = rowBytes; @@ -56,13 +56,22 @@ public: mColorTable = ctable; SkSafeRef(mColorTable); } + + // Need to validate the alpha type to filter against the color type + // to prevent things like a non-opaque RGB565 bitmap + SkAlphaType alphaType; + LOG_ALWAYS_FATAL_IF(!SkColorTypeValidateAlphaType( + newInfo.colorType(), newInfo.alphaType(), &alphaType), + "Failed to validate alpha type!"); + // Dirty hack is dirty // TODO: Figure something out here, Skia's current design makes this // really hard to work with. Skia really, really wants immutable objects, // but with the nested-ref-count hackery going on that's just not // feasible without going insane trying to figure it out SkImageInfo* myInfo = const_cast(&this->info()); - *myInfo = info; + *myInfo = newInfo; + changeAlphaType(alphaType); // Docs say to only call this in the ctor, but we're going to call // it anyway even if this isn't always the ctor. @@ -254,6 +263,14 @@ void Bitmap::reconfigure(const SkImageInfo& info) { reconfigure(info, info.minRowBytes(), nullptr); } +void Bitmap::setAlphaType(SkAlphaType alphaType) { + if (!SkColorTypeValidateAlphaType(info().colorType(), alphaType, &alphaType)) { + return; + } + + mPixelRef->changeAlphaType(alphaType); +} + void Bitmap::detachFromJava() { bool disposeSelf; { @@ -861,10 +878,10 @@ static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle, jboolean hasAlpha, jboolean requestPremul) { LocalScopedBitmap bitmap(bitmapHandle); if (hasAlpha) { - bitmap->peekAtPixelRef()->changeAlphaType( + bitmap->setAlphaType( requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType); } else { - bitmap->peekAtPixelRef()->changeAlphaType(kOpaque_SkAlphaType); + bitmap->setAlphaType(kOpaque_SkAlphaType); } } @@ -873,9 +890,9 @@ static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, LocalScopedBitmap bitmap(bitmapHandle); if (!bitmap->info().isOpaque()) { if (isPremul) { - bitmap->peekAtPixelRef()->changeAlphaType(kPremul_SkAlphaType); + bitmap->setAlphaType(kPremul_SkAlphaType); } else { - bitmap->peekAtPixelRef()->changeAlphaType(kUnpremul_SkAlphaType); + bitmap->setAlphaType(kUnpremul_SkAlphaType); } } } diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h index 95b5fae1734dd..eadba5c0e6341 100644 --- a/core/jni/android/graphics/Bitmap.h +++ b/core/jni/android/graphics/Bitmap.h @@ -71,6 +71,7 @@ public: void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); void reconfigure(const SkImageInfo& info); + void setAlphaType(SkAlphaType alphaType); void getSkBitmap(SkBitmap* outBitmap); void detachFromJava(); From d874f1893c96ee13378bbddf5114540fac9e98f1 Mon Sep 17 00:00:00 2001 From: Guang Zhu Date: Thu, 28 May 2015 13:22:28 -0700 Subject: [PATCH 4/5] lock device orientation during app compatibility test Devices on test benches are usually held sideways, which leads to app rotation during launch. While it's useful to identify such issue from an app perspective, the app compatibility test should be isolated from such noise for the purpose of the testing for basic level of compatibility. Change-Id: I403f96e5d8512ca6a17b05a83d69f4b02f760a32 --- .../src/com/android/compatibilitytest/AppCompatibility.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java index eaff6c74ad302..f81b001cdd18d 100644 --- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java +++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java @@ -17,6 +17,7 @@ package com.android.compatibilitytest; import android.app.ActivityManager; +import android.app.UiAutomation; import android.app.UiModeManager; import android.app.ActivityManager.ProcessErrorStateInfo; import android.app.ActivityManager.RunningTaskInfo; @@ -82,10 +83,12 @@ public class AppCompatibility extends InstrumentationTestCase { if (workspaceLaunchTimeoutMsecs != null) { mWorkspaceLaunchTimeout = Integer.parseInt(workspaceLaunchTimeoutMsecs); } + getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0); } @Override protected void tearDown() throws Exception { + getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE); super.tearDown(); } From d944986fbdb3d45fab9ae4120af76ca4f6b0909c Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Fri, 29 May 2015 14:49:08 -0700 Subject: [PATCH 5/5] Fix API review: Camera prewarm Let the intent receiver of a camea launch intent declare a prewarm service instead of sending broadcasts. Bug: 21347653 Change-Id: I11e31aad4f788ad90eb46a661b819d3e808ddb51 --- api/current.txt | 10 +- api/system-current.txt | 10 +- core/java/android/provider/MediaStore.java | 42 +++---- .../service/media/CameraPrewarmService.java | 96 +++++++++++++++ .../android/keyguard/KeyguardHostView.java | 13 +- .../statusbar/phone/ActivityStarter.java | 7 +- .../phone/KeyguardAffordanceHelper.java | 4 +- .../phone/KeyguardBottomAreaView.java | 115 ++++++++++++++---- .../statusbar/phone/KeyguardBouncer.java | 6 +- .../phone/NotificationPanelView.java | 12 +- .../statusbar/phone/PhoneStatusBar.java | 48 ++++++-- .../phone/StatusBarKeyguardViewManager.java | 5 +- .../statusbar/policy/PreviewInflater.java | 11 +- tests/CameraPrewarmTest/AndroidManifest.xml | 23 ++-- .../CameraPrewarmTest/res/values/strings.xml | 2 +- .../test/cameraprewarm/CameraActivity.java | 2 - ...ewarmReceiver.java => PrewarmService.java} | 20 ++- 17 files changed, 317 insertions(+), 109 deletions(-) create mode 100644 core/java/android/service/media/CameraPrewarmService.java rename tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/{PrewarmReceiver.java => PrewarmService.java} (55%) diff --git a/api/current.txt b/api/current.txt index be6e8736b0177..54647ad8deb3f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -26126,8 +26126,6 @@ package android.provider { method public static java.lang.String getVersion(android.content.Context); field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE"; - field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_COOLDOWN = "android.media.action.STILL_IMAGE_CAMERA_COOLDOWN"; - field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM"; field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE"; field public static final java.lang.String AUTHORITY = "media"; field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit"; @@ -26155,6 +26153,7 @@ package android.provider { field public static final java.lang.String INTENT_ACTION_VIDEO_PLAY_FROM_SEARCH = "android.media.action.VIDEO_PLAY_FROM_SEARCH"; field public static final java.lang.String MEDIA_IGNORE_FILENAME = ".nomedia"; field public static final java.lang.String MEDIA_SCANNER_VOLUME = "volume"; + field public static final java.lang.String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = "android.media.still_image_camera_preview_service"; field public static final java.lang.String UNKNOWN_STRING = ""; } @@ -28713,6 +28712,13 @@ package android.service.dreams { package android.service.media { + public abstract class CameraPrewarmService extends android.app.Service { + ctor public CameraPrewarmService(); + method public android.os.IBinder onBind(android.content.Intent); + method public abstract void onCooldown(boolean); + method public abstract void onPrewarm(); + } + public abstract class MediaBrowserService extends android.app.Service { ctor public MediaBrowserService(); method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); diff --git a/api/system-current.txt b/api/system-current.txt index a072bd7bad4b7..87bdff5d0f29f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -28049,8 +28049,6 @@ package android.provider { method public static java.lang.String getVersion(android.content.Context); field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE"; - field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_COOLDOWN = "android.media.action.STILL_IMAGE_CAMERA_COOLDOWN"; - field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM"; field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE"; field public static final java.lang.String AUTHORITY = "media"; field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit"; @@ -28078,6 +28076,7 @@ package android.provider { field public static final java.lang.String INTENT_ACTION_VIDEO_PLAY_FROM_SEARCH = "android.media.action.VIDEO_PLAY_FROM_SEARCH"; field public static final java.lang.String MEDIA_IGNORE_FILENAME = ".nomedia"; field public static final java.lang.String MEDIA_SCANNER_VOLUME = "volume"; + field public static final java.lang.String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = "android.media.still_image_camera_preview_service"; field public static final java.lang.String UNKNOWN_STRING = ""; } @@ -30739,6 +30738,13 @@ package android.service.dreams { package android.service.media { + public abstract class CameraPrewarmService extends android.app.Service { + ctor public CameraPrewarmService(); + method public android.os.IBinder onBind(android.content.Intent); + method public abstract void onCooldown(boolean); + method public abstract void onPrewarm(); + } + public abstract class MediaBrowserService extends android.app.Service { ctor public MediaBrowserService(); method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 7565654b7ae7e..51dbdee3a23e5 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -22,6 +22,7 @@ import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; +import android.content.Intent; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteException; @@ -33,6 +34,7 @@ import android.media.ThumbnailUtils; import android.net.Uri; import android.os.Environment; import android.os.ParcelFileDescriptor; +import android.service.media.CameraPrewarmService; import android.util.Log; import java.io.FileInputStream; @@ -226,33 +228,24 @@ public final class MediaStore { public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA"; /** - * The name of the Intent action used to indicate that a camera launch might be imminent. This - * broadcast should be targeted to the package that is receiving - * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA} or - * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE}, depending on the context. If such - * intent would launch the resolver activity, this broadcast should not be sent at all. + * Name under which an activity handling {@link #INTENT_ACTION_STILL_IMAGE_CAMERA} or + * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE} publishes the service name for its prewarm + * service. *

- * A receiver of this broadcast should do the absolute minimum amount of work to initialize the - * camera in order to reduce startup time in likely case that shortly after an actual camera - * launch intent would be sent. + * This meta-data should reference the fully qualified class name of the prewarm service + * extending {@link CameraPrewarmService}. *

- * In case the actual intent will not be fired, the target package will receive - * {@link #ACTION_STILL_IMAGE_CAMERA_COOLDOWN}. However, it is recommended that the receiver - * also implements a timeout to close the camera after receiving this intent, as there is no - * guarantee that {@link #ACTION_STILL_IMAGE_CAMERA_COOLDOWN} will be delivered. + * The prewarm service will get bound and receive a prewarm signal + * {@link CameraPrewarmService#onPrewarm()} when a camera launch intent fire might be imminent. + * An application implementing a prewarm service should do the absolute minimum amount of work + * to initialize the camera in order to reduce startup time in likely case that shortly after a + * camera launch intent would be sent. + *

+ * If the camera launch intent gets fired shortly after, the service will be unbound + * asynchronously, without receiving */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM"; - - /** - * The name of the Intent action used to indicate that an imminent camera launch has been - * cancelled by the user. This broadcast should be targeted to the package that has received - * {@link #ACTION_STILL_IMAGE_CAMERA_PREWARM}. - *

- * A receiver of this broadcast should close the camera immediately. - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_STILL_IMAGE_CAMERA_COOLDOWN = "android.media.action.STILL_IMAGE_CAMERA_COOLDOWN"; + public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = + "android.media.still_image_camera_preview_service"; /** * The name of the Intent action used to launch a camera in still image mode @@ -2268,5 +2261,4 @@ public final class MediaStore { } return null; } - } diff --git a/core/java/android/service/media/CameraPrewarmService.java b/core/java/android/service/media/CameraPrewarmService.java new file mode 100644 index 0000000000000..335b00a508792 --- /dev/null +++ b/core/java/android/service/media/CameraPrewarmService.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.service.media; + +import android.app.Service; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; + +/** + * Extend this class to implement a camera prewarm service. See + * {@link android.provider.MediaStore#META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE}. + */ +public abstract class CameraPrewarmService extends Service { + + /** + * Intent action to bind the service as a prewarm service. + * @hide + */ + public static final String ACTION_PREWARM = + "android.service.media.CameraPrewarmService.ACTION_PREWARM"; + + /** + * Message sent by the client indicating that the camera intent has been fired. + * @hide + */ + public static final int MSG_CAMERA_FIRED = 1; + + private final Handler mHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_CAMERA_FIRED: + mCameraIntentFired = true; + break; + default: + super.handleMessage(msg); + } + } + }; + private boolean mCameraIntentFired; + + @Override + public IBinder onBind(Intent intent) { + if (ACTION_PREWARM.equals(intent.getAction())) { + onPrewarm(); + return new Messenger(mHandler).getBinder(); + } else { + return null; + } + } + + @Override + public boolean onUnbind(Intent intent) { + if (ACTION_PREWARM.equals(intent.getAction())) { + onCooldown(mCameraIntentFired); + } + return false; + } + + /** + * Called when the camera should be prewarmed. + */ + public abstract void onPrewarm(); + + /** + * Called when prewarm phase is done, either because the camera launch intent has been fired + * at this point or prewarm is no longer needed. A client should close the camera + * immediately in the latter case. + *

+ * In case the camera launch intent has been fired, there is no guarantee about the ordering + * of these two events. Cooldown might happen either before or after the activity has been + * created that handles the camera intent. + * + * @param cameraIntentFired Indicates whether the intent to launch the camera has been + * fired. + */ + public abstract void onCooldown(boolean cameraIntentFired); +} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java index 2cf30ba83e5ca..cd4b24a9e1782 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java @@ -60,6 +60,7 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback { protected ViewMediatorCallback mViewMediatorCallback; protected LockPatternUtils mLockPatternUtils; private OnDismissAction mDismissAction; + private Runnable mCancelAction; private final KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @@ -126,8 +127,17 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback { * * @param action */ - public void setOnDismissAction(OnDismissAction action) { + public void setOnDismissAction(OnDismissAction action, Runnable cancelAction) { + if (mCancelAction != null) { + mCancelAction.run(); + mCancelAction = null; + } mDismissAction = action; + mCancelAction = cancelAction; + } + + public void cancelDismissAction() { + setOnDismissAction(null, null); } @Override @@ -197,6 +207,7 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback { if (mDismissAction != null) { deferKeyguardDone = mDismissAction.onDismiss(); mDismissAction = null; + mCancelAction = null; } if (mViewMediatorCallback != null) { if (deferKeyguardDone) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java index ee5eb38621e30..9ef320bc3fd9d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java @@ -24,6 +24,11 @@ import android.content.Intent; * Keyguard. */ public interface ActivityStarter { - public void startActivity(Intent intent, boolean dismissShade); + void startActivity(Intent intent, boolean dismissShade); + void startActivity(Intent intent, boolean dismissShade, Callback callback); void preventNextAnimation(); + + interface Callback { + void onActivityStarted(int resultCode); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java index 8bffdc91e79b2..64735eec40748 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java @@ -160,7 +160,7 @@ public class KeyguardAffordanceHelper { } else { mTouchSlopExeeded = false; } - mCallback.onSwipingStarted(targetView == mLeftIcon); + mCallback.onSwipingStarted(targetView == mRightIcon); mSwipingInProgress = true; mTargetedView = targetView; mInitialTouchX = x; @@ -550,7 +550,7 @@ public class KeyguardAffordanceHelper { float getMaxTranslationDistance(); - void onSwipingStarted(boolean isRightwardMotion); + void onSwipingStarted(boolean rightIcon); void onSwipingAborted(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index adee5a8ae6fbb..3258a9f932015 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -16,12 +16,17 @@ package com.android.systemui.statusbar.phone; +import android.app.ActivityManager; import android.app.ActivityManagerNative; +import android.app.Application; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; @@ -29,9 +34,13 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.AsyncTask; import android.os.Bundle; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; import android.os.RemoteException; import android.os.UserHandle; import android.provider.MediaStore; +import android.service.media.CameraPrewarmService; import android.telecom.TelecomManager; import android.util.AttributeSet; import android.util.Log; @@ -100,7 +109,23 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private PhoneStatusBar mPhoneStatusBar; private final Interpolator mLinearOutSlowInInterpolator; - private boolean mPrewarmSent; + private boolean mPrewarmBound; + private Messenger mPrewarmMessenger; + private final ServiceConnection mPrewarmConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mPrewarmMessenger = new Messenger(service); + mPrewarmBound = true; + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mPrewarmBound = false; + mPrewarmMessenger = null; + } + }; + private boolean mLeftIsVoiceAssist; private AssistManager mAssistManager; @@ -343,37 +368,44 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser()); } - public void prewarmCamera() { + public void bindCameraPrewarmService() { Intent intent = getCameraIntent(); - String targetPackage = PreviewInflater.getTargetPackage(mContext, intent, + ActivityInfo targetInfo = PreviewInflater.getTargetActivityInfo(mContext, intent, KeyguardUpdateMonitor.getCurrentUser()); - if (targetPackage != null) { - Intent prewarm = new Intent(MediaStore.ACTION_STILL_IMAGE_CAMERA_PREWARM); - prewarm.setPackage(targetPackage); - mPrewarmSent = true; - mContext.sendBroadcast(prewarm); + if (targetInfo != null) { + String clazz = targetInfo.metaData.getString( + MediaStore.META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE); + if (clazz != null) { + Intent serviceIntent = new Intent(); + serviceIntent.setClassName(targetInfo.packageName, clazz); + serviceIntent.setAction(CameraPrewarmService.ACTION_PREWARM); + try { + getContext().bindServiceAsUser(serviceIntent, mPrewarmConnection, + Context.BIND_AUTO_CREATE, new UserHandle(UserHandle.USER_CURRENT)); + } catch (SecurityException e) { + Log.w(TAG, "Unable to bind to prewarm service package=" + targetInfo.packageName + + " class=" + clazz, e); + } + } } } - public void maybeCooldownCamera() { - if (!mPrewarmSent) { - return; - } - mPrewarmSent = false; - Intent intent = getCameraIntent(); - String targetPackage = PreviewInflater.getTargetPackage(mContext, intent, - KeyguardUpdateMonitor.getCurrentUser()); - if (targetPackage != null) { - Intent prewarm = new Intent(MediaStore.ACTION_STILL_IMAGE_CAMERA_COOLDOWN); - prewarm.setPackage(targetPackage); - mContext.sendBroadcast(prewarm); + public void unbindCameraPrewarmService(boolean launched) { + if (mPrewarmBound) { + if (launched) { + try { + mPrewarmMessenger.send(Message.obtain(null /* handler */, + CameraPrewarmService.MSG_CAMERA_FIRED)); + } catch (RemoteException e) { + Log.w(TAG, "Error sending camera fired message", e); + } + } + mContext.unbindService(mPrewarmConnection); + mPrewarmBound = false; } } public void launchCamera() { - - // Reset prewarm state. - mPrewarmSent = false; final Intent intent = getCameraIntent(); boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity( mContext, intent, KeyguardUpdateMonitor.getCurrentUser()); @@ -381,18 +413,47 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL AsyncTask.execute(new Runnable() { @Override public void run() { - mContext.startActivityAsUser(intent, UserHandle.CURRENT); + int result = ActivityManager.START_CANCELED; + try { + result = ActivityManagerNative.getDefault().startActivityAsUser( + null, getContext().getBasePackageName(), + intent, + intent.resolveTypeIfNeeded(getContext().getContentResolver()), + null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, + UserHandle.CURRENT.getIdentifier()); + } catch (RemoteException e) { + Log.w(TAG, "Unable to start camera activity", e); + } mActivityStarter.preventNextAnimation(); + final boolean launched = isSuccessfulLaunch(result); + post(new Runnable() { + @Override + public void run() { + unbindCameraPrewarmService(launched); + } + }); } }); } else { // We need to delay starting the activity because ResolverActivity finishes itself if // launched behind lockscreen. - mActivityStarter.startActivity(intent, false /* dismissShade */); + mActivityStarter.startActivity(intent, false /* dismissShade */, + new ActivityStarter.Callback() { + @Override + public void onActivityStarted(int resultCode) { + unbindCameraPrewarmService(isSuccessfulLaunch(resultCode)); + } + }); } } + private static boolean isSuccessfulLaunch(int result) { + return result == ActivityManager.START_SUCCESS + || result == ActivityManager.START_DELIVERED_TO_TOP + || result == ActivityManager.START_TASK_TO_FRONT; + } + public void launchLeftAffordance() { if (mLeftIsVoiceAssist) { launchVoiceAssist(); @@ -412,8 +473,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL if (mPhoneStatusBar.isKeyguardCurrentlySecure()) { AsyncTask.execute(runnable); } else { - mPhoneStatusBar.executeRunnableDismissingKeyguard(runnable, false /* dismissShade */, - false /* afterKeyguardGone */); + mPhoneStatusBar.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */, + false /* dismissShade */, false /* afterKeyguardGone */); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 262d955957fe9..3d57d54ef822d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -95,16 +95,16 @@ public class KeyguardBouncer { mShowingSoon = false; } - public void showWithDismissAction(OnDismissAction r) { + public void showWithDismissAction(OnDismissAction r, Runnable cancelAction) { ensureView(); - mKeyguardView.setOnDismissAction(r); + mKeyguardView.setOnDismissAction(r, cancelAction); show(false /* resetSecuritySelection */); } public void hide(boolean destroyView) { cancelShowRunnable(); if (mKeyguardView != null) { - mKeyguardView.setOnDismissAction(null); + mKeyguardView.cancelDismissAction(); mKeyguardView.cleanUp(); } if (destroyView) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 5d48190dbf99c..2fe98bb5748d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -1934,12 +1934,12 @@ public class NotificationPanelView extends PanelView implements } @Override - public void onSwipingStarted(boolean isRightwardMotion) { - boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? isRightwardMotion - : !isRightwardMotion; - if (!start) { + public void onSwipingStarted(boolean rightIcon) { + boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon + : rightIcon; + if (camera) { mSecureCameraLaunchManager.onSwipingStarted(); - mKeyguardBottomArea.prewarmCamera(); + mKeyguardBottomArea.bindCameraPrewarmService(); } requestDisallowInterceptTouchEvent(true); mOnlyAffordanceInThisMotion = true; @@ -1948,7 +1948,7 @@ public class NotificationPanelView extends PanelView implements @Override public void onSwipingAborted() { - mKeyguardBottomArea.maybeCooldownCamera(); + mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 1e4aa61fd1b8b..e6089ef9744de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -1825,6 +1825,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, startActivityDismissingKeyguard(intent, false, dismissShade); } + @Override + public void startActivity(Intent intent, boolean dismissShade, Callback callback) { + startActivityDismissingKeyguard(intent, false, dismissShade, callback); + } + @Override public void preventNextAnimation() { overrideActivityPendingAppTransition(true /* keyguardShowing */); @@ -2707,7 +2712,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, - final boolean dismissShade) { + boolean dismissShade) { + startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, null /* callback */); + } + + public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, + final boolean dismissShade, final Callback callback) { if (onlyProvisioned && !isDeviceProvisioned()) return; final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( @@ -2717,16 +2727,35 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void run() { intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivityAsUser( - intent, new UserHandle(UserHandle.USER_CURRENT)); + int result = ActivityManager.START_CANCELED; + try { + result = ActivityManagerNative.getDefault().startActivityAsUser( + null, mContext.getBasePackageName(), + intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, + UserHandle.CURRENT.getIdentifier()); + } catch (RemoteException e) { + Log.w(TAG, "Unable to start activity", e); + } overrideActivityPendingAppTransition( keyguardShowing && !afterKeyguardGone); + if (callback != null) { + callback.onActivityStarted(result); + } } }; - executeRunnableDismissingKeyguard(runnable, dismissShade, afterKeyguardGone); + Runnable cancelRunnable = new Runnable() { + @Override + public void run() { + callback.onActivityStarted(ActivityManager.START_CANCELED); + } + }; + executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade, afterKeyguardGone); } public void executeRunnableDismissingKeyguard(final Runnable runnable, + final Runnable cancelAction, final boolean dismissShade, final boolean afterKeyguardGone) { final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); @@ -2753,7 +2782,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } return true; } - }, afterKeyguardGone); + }, cancelAction, afterKeyguardGone); } private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -2813,10 +2842,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - protected void dismissKeyguardThenExecute(final OnDismissAction action, + protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) { + dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone); + } + + private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction, boolean afterKeyguardGone) { if (mStatusBarKeyguardViewManager.isShowing()) { - mStatusBarKeyguardViewManager.dismissWithAction(action, afterKeyguardGone); + mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, + afterKeyguardGone); } else { action.onDismiss(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 0caf51af9807e..6cb890a281914 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -126,10 +126,11 @@ public class StatusBarKeyguardViewManager { updateStates(); } - public void dismissWithAction(OnDismissAction r, boolean afterKeyguardGone) { + public void dismissWithAction(OnDismissAction r, Runnable cancelAction, + boolean afterKeyguardGone) { if (mShowing) { if (!afterKeyguardGone) { - mBouncer.showWithDismissAction(r); + mBouncer.showWithDismissAction(r, cancelAction); } else { mBouncer.show(false /* resetSecuritySelection */); mAfterKeyguardGoneAction = r; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java index 4269c19568323..93d0ec362ade1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.policy; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; @@ -138,14 +139,14 @@ public class PreviewInflater { public static boolean wouldLaunchResolverActivity(Context ctx, Intent intent, int currentUserId) { - return getTargetPackage(ctx, intent, currentUserId) == null; + return getTargetActivityInfo(ctx, intent, currentUserId) == null; } /** - * @return the target package of the intent it resolves to a specific package or {@code null} if - * it resolved to the resolver activity + * @return the target activity info of the intent it resolves to a specific package or + * {@code null} if it resolved to the resolver activity */ - public static String getTargetPackage(Context ctx, Intent intent, + public static ActivityInfo getTargetActivityInfo(Context ctx, Intent intent, int currentUserId) { PackageManager packageManager = ctx.getPackageManager(); final List appList = packageManager.queryIntentActivitiesAsUser( @@ -158,7 +159,7 @@ public class PreviewInflater { if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) { return null; } else { - return resolved.activityInfo.packageName; + return resolved.activityInfo; } } diff --git a/tests/CameraPrewarmTest/AndroidManifest.xml b/tests/CameraPrewarmTest/AndroidManifest.xml index eb402008319d0..11b2686d29322 100644 --- a/tests/CameraPrewarmTest/AndroidManifest.xml +++ b/tests/CameraPrewarmTest/AndroidManifest.xml @@ -21,14 +21,14 @@ - - - - + + + + - - - - - - - - + + diff --git a/tests/CameraPrewarmTest/res/values/strings.xml b/tests/CameraPrewarmTest/res/values/strings.xml index 11f7ac7b85985..fe39ac1d152e6 100644 --- a/tests/CameraPrewarmTest/res/values/strings.xml +++ b/tests/CameraPrewarmTest/res/values/strings.xml @@ -16,6 +16,6 @@ --> - Assistant + Camera Prewarm test Orilla Search Engine diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java index 4d22234b1ecc7..0b43666a54534 100644 --- a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java +++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java @@ -19,7 +19,6 @@ package com.google.android.test.cameraprewarm; import android.app.Activity; import android.os.Bundle; import android.util.Log; -import android.view.WindowManager; import com.google.android.test.cameraprewarm.R; @@ -31,7 +30,6 @@ public class CameraActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.camera_activity); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); Log.i(TAG, "Activity created"); } } diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmService.java similarity index 55% rename from tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java rename to tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmService.java index d49f96d0a38c2..d080b1a50d37e 100644 --- a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java +++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmService.java @@ -16,20 +16,18 @@ package com.google.android.test.cameraprewarm; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.provider.MediaStore; +import android.service.media.CameraPrewarmService; import android.util.Log; -public class PrewarmReceiver extends BroadcastReceiver { +public class PrewarmService extends CameraPrewarmService { @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(MediaStore.ACTION_STILL_IMAGE_CAMERA_PREWARM)) { - Log.i(CameraActivity.TAG, "Prewarm received"); - } else if (intent.getAction().equals(MediaStore.ACTION_STILL_IMAGE_CAMERA_COOLDOWN)){ - Log.i(CameraActivity.TAG, "Cooldown received"); - } + public void onPrewarm() { + Log.i("PrewarmService", "Warming up"); + } + + @Override + public void onCooldown(boolean cameraIntentFired) { + Log.i("PrewarmService", "Cooling down fired=" + cameraIntentFired); } }