SecureCameraLaunchManager.javaAPI DocAndroid 5.1 API13254Thu Mar 12 22:22:42 GMT


public class SecureCameraLaunchManager extends Object
Handles launching the secure camera properly even when other applications may be using the camera hardware. When other applications (e.g., Face Unlock) are using the camera, they must close the camera to allow the secure camera to open it. Since we want to minimize the delay when opening the secure camera, other apps should close the camera at the first possible opportunity (i.e., as soon as the user begins swiping to go to the secure camera). If the camera is unavailable when the user begins to swipe, the SecureCameraLaunchManager sends a broadcast to tell other apps to close the camera. When and if the user completes their swipe to launch the secure camera, the SecureCameraLaunchManager delays launching the secure camera until a callback indicates that the camera has become available. If it doesn't receive that callback within a specified timeout period, the secure camera is launched anyway. Ideally, the secure camera would handle waiting for the camera to become available. This allows some of the time necessary to close the camera to happen in parallel with starting the secure camera app. We can't rely on all third-party camera apps to handle this. However, an app can put in its meta-data to indicate that it will be responsible for waiting for the camera to become available. It is assumed that the functions in this class, including the constructor, will be called from the UI thread.

Fields Summary
private static final boolean
private static final String
private static final String
private static final String
private static final int
private android.content.Context
private android.os.Handler
private KeyguardBottomAreaView
private android.hardware.camera2.CameraManager
private CameraAvailabilityCallback
private Map
private boolean
private Runnable
Constructors Summary
public SecureCameraLaunchManager(android.content.Context context, KeyguardBottomAreaView keyguardBottomArea)

        mContext = context;
        mHandler = new Handler();
        mLockPatternUtils = new LockPatternUtils(context);
        mKeyguardBottomArea = keyguardBottomArea;

        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
        mCameraAvailabilityCallback = new CameraAvailabilityCallback();

        // An onCameraAvailable() or onCameraUnavailable() callback will be received for each camera
        // when the availability callback is registered, thus initializing the map.
        // Keeping track of the state of all cameras using the onCameraAvailable() and
        // onCameraUnavailable() callbacks can get messy when dealing with hot-pluggable cameras.
        // However, we have a timeout in place such that we will never hang waiting for cameras.
        mCameraAvailabilityMap = new HashMap<String, Boolean>();

        mWaitingToLaunchSecureCamera = false;
        mLaunchCameraRunnable = new Runnable() {
                public void run() {
                    if (mWaitingToLaunchSecureCamera) {
                        Log.w(TAG, "Timeout waiting for camera availability");
                        mWaitingToLaunchSecureCamera = false;
Methods Summary
private booleanareAllCamerasAvailable()
Returns true if all of the cameras we are tracking are currently available.

        for (boolean cameraAvailable: mCameraAvailabilityMap.values()) {
            if (!cameraAvailable) {
                return false;
        return true;
public voidcreate()
Initializes the SecureCameraManager and starts listening for camera availability.

        mCameraManager.registerAvailabilityCallback(mCameraAvailabilityCallback, mHandler);
public voiddestroy()
Stops listening for camera availability and cleans up the SecureCameraManager.

public voidonSwipingStarted()
Called when the user is starting to swipe horizontally, possibly to start the secure camera. Although this swipe ultimately may not result in the secure camera opening, we need to stop all other camera usage (e.g., Face Unlock) as soon as possible. We send out a broadcast to notify other apps that they should close the camera immediately. The broadcast is sent even if the camera appears to be available, because there could be an app that is about to open the camera.

        if (DEBUG) Log.d(TAG, "onSwipingStarted");
        AsyncTask.execute(new Runnable() {
                public void run() {
                    Intent intent = new Intent();
public voidstartSecureCameraLaunch()
Called when the secure camera should be started. If the camera is available or the secure camera app has indicated that it will wait for camera availability, the secure camera app is launched immediately. Otherwise, we wait for the camera to become available (or timeout) before launching the secure camera.

        if (DEBUG) Log.d(TAG, "startSecureCameraLunch");
        if (areAllCamerasAvailable() || targetWillWaitForCameraAvailable()) {
        } else {
            mWaitingToLaunchSecureCamera = true;
            mHandler.postDelayed(mLaunchCameraRunnable, CAMERA_AVAILABILITY_TIMEOUT_MS);
private booleantargetWillWaitForCameraAvailable()
Determines if the secure camera app will wait for the camera hardware to become available before trying to open the camera. If so, we can fire off an intent to start the secure camera app before the camera is available. Otherwise, it is our responsibility to wait for the camera hardware to become available before firing off the intent to start the secure camera. Ideally we are able to fire off the secure camera intent as early as possibly so that, if the camera is closing, it can continue to close while the secure camera app is opening. This improves secure camera startup time.

        // Create intent that would launch the secure camera.
        Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
        PackageManager packageManager = mContext.getPackageManager();

        // Get the list of applications that can handle the intent.
        final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
                intent, PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser());
        if (appList.size() == 0) {
            if (DEBUG) Log.d(TAG, "No targets found for secure camera intent");
            return false;

        // Get the application that the intent resolves to.
        ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,

        if (resolved == null || resolved.activityInfo == null) {
            return false;

        // If we would need to launch the resolver activity, then we can't assume that the target
        // is one that would wait for the camera.
        if (wouldLaunchResolverActivity(resolved, appList)) {
            if (DEBUG) Log.d(TAG, "Secure camera intent would launch resolver");
            return false;

        // If the target doesn't have meta-data we must assume it won't wait for the camera.
        if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
            if (DEBUG) Log.d(TAG, "No meta-data found for secure camera application");
            return false;

        // Check the secure camera app meta-data to see if it indicates that it will wait for the
        // camera to become available.
        boolean willWaitForCameraAvailability =

        if (DEBUG) Log.d(TAG, "Target will wait for camera: " + willWaitForCameraAvailability);

        return willWaitForCameraAvailability;
private booleanwouldLaunchResolverActivity( resolved, java.util.List appList)
Determines if the activity that would be launched by the intent is the ResolverActivity.

        // If the list contains the resolved activity, then it can't be the ResolverActivity itself.
        for (int i = 0; i < appList.size(); i++) {
            ResolveInfo tmp = appList.get(i);
            if (
                    && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
                return false;
        return true;