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
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 ee5eb38..9ef320bc 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 @@
  * 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 8bffdc9..64735ee 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 @@
                 } else {
                     mTouchSlopExeeded = false;
                 }
-                mCallback.onSwipingStarted(targetView == mLeftIcon);
+                mCallback.onSwipingStarted(targetView == mRightIcon);
                 mSwipingInProgress = true;
                 mTargetedView = targetView;
                 mInitialTouchX = x;
@@ -550,7 +550,7 @@
 
         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 adee5a8..3258a9f 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.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 @@
     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 @@
         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 @@
             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 @@
         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 262d955..3d57d54 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 @@
         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 5d48190..2fe98bb 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 @@
     }
 
     @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 @@
 
     @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 1e4aa61..e6089ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1826,6 +1826,11 @@
     }
 
     @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 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 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 @@
                 }
                 return true;
             }
-        }, afterKeyguardGone);
+        }, cancelAction, afterKeyguardGone);
     }
 
     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -2813,10 +2842,15 @@
     }
 
     @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 0caf51a..6cb890a 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 @@
         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 4269c19..93d0ec3 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 @@
 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 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<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
@@ -158,7 +159,7 @@
         if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) {
             return null;
         } else {
-            return resolved.activityInfo.packageName;
+            return resolved.activityInfo;
         }
     }