KeyguardActivityLauncher.java revision 57f928fc4afc02d2fe45523efea687eee7f86c02
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.policy.impl.keyguard;
18
19import android.app.ActivityManagerNative;
20import android.app.ActivityOptions;
21import android.app.IActivityManager.WaitResult;
22import android.content.ActivityNotFoundException;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.os.Bundle;
28import android.os.Handler;
29import android.os.RemoteException;
30import android.os.SystemClock;
31import android.os.UserHandle;
32import android.provider.MediaStore;
33import android.util.Log;
34import android.view.WindowManager;
35
36import com.android.internal.widget.LockPatternUtils;
37
38import java.util.List;
39
40public abstract class KeyguardActivityLauncher {
41    private static final String TAG = KeyguardActivityLauncher.class.getSimpleName();
42    private static final boolean DEBUG = KeyguardHostView.DEBUG;
43    private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
44    private static final Intent SECURE_CAMERA_INTENT =
45            new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
46                    .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
47    private static final Intent INSECURE_CAMERA_INTENT =
48            new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
49
50    abstract Context getContext();
51
52    abstract KeyguardSecurityCallback getCallback();
53
54    abstract LockPatternUtils getLockPatternUtils();
55
56    public static class CameraWidgetInfo {
57        public String contextPackage;
58        public int layoutId;
59    }
60
61    public CameraWidgetInfo getCameraWidgetInfo() {
62        CameraWidgetInfo info = new CameraWidgetInfo();
63        Intent intent = getCameraIntent();
64        PackageManager packageManager = getContext().getPackageManager();
65        final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
66                intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
67        if (appList.size() == 0) {
68            if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found");
69            return null;
70        }
71        ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
72                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
73                getLockPatternUtils().getCurrentUser());
74        if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved);
75        if (wouldLaunchResolverActivity(resolved, appList)) {
76            if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver");
77            return info;
78        }
79        if (resolved == null || resolved.activityInfo == null) {
80            return null;
81        }
82        if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
83            if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found");
84            return info;
85        }
86        int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
87        if (layoutId == 0) {
88            if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified");
89            return info;
90        }
91        info.contextPackage = resolved.activityInfo.packageName;
92        info.layoutId = layoutId;
93        return info;
94    }
95
96    public void launchCamera(Handler worker, Runnable onSecureCameraStarted) {
97        LockPatternUtils lockPatternUtils = getLockPatternUtils();
98        if (lockPatternUtils.isSecure()) {
99            // Launch the secure version of the camera
100            if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
101                // TODO: Show disambiguation dialog instead.
102                // For now, we'll treat this like launching any other app from secure keyguard.
103                // When they do, user sees the system's ResolverActivity which lets them choose
104                // which secure camera to use.
105                launchActivity(SECURE_CAMERA_INTENT, false, false, null, null);
106            } else {
107                launchActivity(SECURE_CAMERA_INTENT, true, false, worker, onSecureCameraStarted);
108            }
109        } else {
110            // Launch the normal camera
111            launchActivity(INSECURE_CAMERA_INTENT, false, false, null, null);
112        }
113    }
114
115    /**
116     * Launches the said intent for the current foreground user.
117     *
118     * @param intent
119     * @param showsWhileLocked true if the activity can be run on top of keyguard.
120     *   See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
121     * @param useDefaultAnimations true if default transitions should be used, else suppressed.
122     * @param worker if supplied along with onStarted, used to launch the blocking activity call.
123     * @param onStarted if supplied along with worker, called after activity is started.
124     */
125    public void launchActivity(final Intent intent,
126            boolean showsWhileLocked,
127            boolean useDefaultAnimations,
128            final Handler worker,
129            final Runnable onStarted) {
130        final Context context = getContext();
131        final Bundle animation = ActivityOptions.makeCustomAnimation(context, 0, 0).toBundle();
132        LockPatternUtils lockPatternUtils = getLockPatternUtils();
133        intent.addFlags(
134                Intent.FLAG_ACTIVITY_NEW_TASK
135                | Intent.FLAG_ACTIVITY_SINGLE_TOP
136                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
137        boolean isSecure = lockPatternUtils.isSecure();
138        if (!isSecure || showsWhileLocked) {
139            if (!isSecure) try {
140                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
141            } catch (RemoteException e) {
142                Log.w(TAG, "can't dismiss keyguard on launch");
143            }
144            try {
145                if (DEBUG) Log.d(TAG, String.format("Starting activity for intent %s at %s",
146                        intent, SystemClock.uptimeMillis()));
147                startActivityForCurrentUser(intent, animation, worker, onStarted);
148            } catch (ActivityNotFoundException e) {
149                Log.w(TAG, "Activity not found for intent + " + intent.getAction());
150            }
151        } else {
152            // Create a runnable to start the activity and ask the user to enter their
153            // credentials.
154            KeyguardSecurityCallback callback = getCallback();
155            callback.setOnDismissRunnable(new Runnable() {
156                @Override
157                public void run() {
158                    startActivityForCurrentUser(intent, animation, worker, onStarted);
159                }
160            });
161            callback.dismiss(false);
162        }
163    }
164
165    private void startActivityForCurrentUser(final Intent intent, final Bundle options,
166            Handler worker, final Runnable onStarted) {
167        final UserHandle user = new UserHandle(UserHandle.USER_CURRENT);
168        if (worker == null || onStarted == null) {
169            getContext().startActivityAsUser(intent, options, user);
170            return;
171        }
172        // if worker + onStarted are supplied, run blocking activity launch call in the background
173        worker.post(new Runnable(){
174            @Override
175            public void run() {
176                try {
177                    WaitResult result = ActivityManagerNative.getDefault().startActivityAndWait(
178                            null /*caller*/,
179                            intent,
180                            intent.resolveTypeIfNeeded(getContext().getContentResolver()),
181                            null /*resultTo*/,
182                            null /*resultWho*/,
183                            0 /*requestCode*/,
184                            Intent.FLAG_ACTIVITY_NEW_TASK,
185                            null /*profileFile*/,
186                            null /*profileFd*/,
187                            options,
188                            user.getIdentifier());
189                    if (DEBUG) Log.d(TAG, String.format("waitResult[%s,%s,%s,%s] at %s",
190                            result.result, result.thisTime, result.totalTime, result.who,
191                            SystemClock.uptimeMillis()));
192                } catch (RemoteException e) {
193                    Log.w(TAG, "Error starting activity", e);
194                    return;
195                }
196                try {
197                    onStarted.run();
198                } catch (Throwable t) {
199                    Log.w(TAG, "Error running onStarted callback", t);
200                }
201            }});
202    }
203
204    private Intent getCameraIntent() {
205        return getLockPatternUtils().isSecure() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
206    }
207
208    private boolean wouldLaunchResolverActivity(Intent intent) {
209        PackageManager packageManager = getContext().getPackageManager();
210        ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
211                PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
212        List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
213                intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
214        return wouldLaunchResolverActivity(resolved, appList);
215    }
216
217    private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
218        // If the list contains the above resolved activity, then it can't be
219        // ResolverActivity itself.
220        for (int i = 0; i < appList.size(); i++) {
221            ResolveInfo tmp = appList.get(i);
222            if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
223                    && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
224                return false;
225            }
226        }
227        return true;
228    }
229}
230