blob: 0a915eaf688e8c2b844d3155be59dba06fb47b2c [file] [log] [blame]
John Spurlockdcc96812012-10-25 14:35:19 -04001/*
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
Jim Miller5ecd8112013-01-09 18:50:26 -080017package com.android.keyguard;
18
19import com.android.internal.widget.LockPatternUtils;
John Spurlockdcc96812012-10-25 14:35:19 -040020
21import android.app.ActivityManagerNative;
Adam Powellcfc30862012-10-29 18:21:31 -070022import android.app.ActivityOptions;
John Spurlock57f928f2012-11-02 13:09:25 -040023import android.app.IActivityManager.WaitResult;
John Spurlock65cbcbe2012-11-06 11:39:34 -050024import android.appwidget.AppWidgetManager;
25import android.appwidget.AppWidgetProviderInfo;
John Spurlockdcc96812012-10-25 14:35:19 -040026import android.content.ActivityNotFoundException;
27import android.content.Context;
28import android.content.Intent;
29import android.content.pm.PackageManager;
30import android.content.pm.ResolveInfo;
Adam Powellcfc30862012-10-29 18:21:31 -070031import android.os.Bundle;
John Spurlock57f928f2012-11-02 13:09:25 -040032import android.os.Handler;
John Spurlockdcc96812012-10-25 14:35:19 -040033import android.os.RemoteException;
John Spurlockdbe24b72012-11-01 13:01:05 -040034import android.os.SystemClock;
John Spurlockdcc96812012-10-25 14:35:19 -040035import android.os.UserHandle;
36import android.provider.MediaStore;
37import android.util.Log;
38import android.view.WindowManager;
39
Jim Miller5ecd8112013-01-09 18:50:26 -080040import com.android.keyguard.KeyguardHostView.OnDismissAction;
John Spurlockdcc96812012-10-25 14:35:19 -040041
42import java.util.List;
43
44public abstract class KeyguardActivityLauncher {
45 private static final String TAG = KeyguardActivityLauncher.class.getSimpleName();
46 private static final boolean DEBUG = KeyguardHostView.DEBUG;
47 private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
48 private static final Intent SECURE_CAMERA_INTENT =
49 new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
50 .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
51 private static final Intent INSECURE_CAMERA_INTENT =
52 new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
53
54 abstract Context getContext();
55
56 abstract KeyguardSecurityCallback getCallback();
57
58 abstract LockPatternUtils getLockPatternUtils();
59
60 public static class CameraWidgetInfo {
61 public String contextPackage;
62 public int layoutId;
63 }
64
65 public CameraWidgetInfo getCameraWidgetInfo() {
66 CameraWidgetInfo info = new CameraWidgetInfo();
67 Intent intent = getCameraIntent();
68 PackageManager packageManager = getContext().getPackageManager();
69 final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
70 intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
71 if (appList.size() == 0) {
72 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found");
73 return null;
74 }
75 ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
76 PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
77 getLockPatternUtils().getCurrentUser());
78 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved);
79 if (wouldLaunchResolverActivity(resolved, appList)) {
80 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver");
81 return info;
82 }
83 if (resolved == null || resolved.activityInfo == null) {
84 return null;
85 }
86 if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
87 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found");
88 return info;
89 }
90 int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
91 if (layoutId == 0) {
92 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified");
93 return info;
94 }
95 info.contextPackage = resolved.activityInfo.packageName;
96 info.layoutId = layoutId;
97 return info;
98 }
99
John Spurlock57f928f2012-11-02 13:09:25 -0400100 public void launchCamera(Handler worker, Runnable onSecureCameraStarted) {
John Spurlockdcc96812012-10-25 14:35:19 -0400101 LockPatternUtils lockPatternUtils = getLockPatternUtils();
Ruben Brunk21a99dd2013-11-05 18:29:06 -0800102
103 // Workaround to avoid camera release/acquisition race when resuming face unlock
104 // after showing lockscreen camera (bug 11063890).
105 KeyguardUpdateMonitor.getInstance(getContext()).setAlternateUnlockEnabled(false);
106
John Spurlockdcc96812012-10-25 14:35:19 -0400107 if (lockPatternUtils.isSecure()) {
108 // Launch the secure version of the camera
109 if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
110 // TODO: Show disambiguation dialog instead.
111 // For now, we'll treat this like launching any other app from secure keyguard.
112 // When they do, user sees the system's ResolverActivity which lets them choose
113 // which secure camera to use.
John Spurlock57f928f2012-11-02 13:09:25 -0400114 launchActivity(SECURE_CAMERA_INTENT, false, false, null, null);
John Spurlockdcc96812012-10-25 14:35:19 -0400115 } else {
John Spurlock57f928f2012-11-02 13:09:25 -0400116 launchActivity(SECURE_CAMERA_INTENT, true, false, worker, onSecureCameraStarted);
John Spurlockdcc96812012-10-25 14:35:19 -0400117 }
118 } else {
John Spurlockdcc96812012-10-25 14:35:19 -0400119 // Launch the normal camera
John Spurlock57f928f2012-11-02 13:09:25 -0400120 launchActivity(INSECURE_CAMERA_INTENT, false, false, null, null);
John Spurlockdcc96812012-10-25 14:35:19 -0400121 }
122 }
123
John Spurlock65cbcbe2012-11-06 11:39:34 -0500124 public void launchWidgetPicker(int appWidgetId) {
125 Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK);
126
127 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
128 pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false);
129 pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER,
130 AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
131
132 Bundle options = new Bundle();
133 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
134 AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
135 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
136 pickIntent.addFlags(
137 Intent.FLAG_ACTIVITY_NEW_TASK
138 | Intent.FLAG_ACTIVITY_SINGLE_TOP
139 | Intent.FLAG_ACTIVITY_CLEAR_TOP
140 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
141
142 launchActivity(pickIntent, false, false, null, null);
143 }
144
John Spurlockdcc96812012-10-25 14:35:19 -0400145 /**
146 * Launches the said intent for the current foreground user.
John Spurlock57f928f2012-11-02 13:09:25 -0400147 *
John Spurlockdcc96812012-10-25 14:35:19 -0400148 * @param intent
149 * @param showsWhileLocked true if the activity can be run on top of keyguard.
John Spurlock57f928f2012-11-02 13:09:25 -0400150 * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
151 * @param useDefaultAnimations true if default transitions should be used, else suppressed.
152 * @param worker if supplied along with onStarted, used to launch the blocking activity call.
153 * @param onStarted if supplied along with worker, called after activity is started.
John Spurlockdcc96812012-10-25 14:35:19 -0400154 */
John Spurlock57f928f2012-11-02 13:09:25 -0400155 public void launchActivity(final Intent intent,
156 boolean showsWhileLocked,
157 boolean useDefaultAnimations,
158 final Handler worker,
159 final Runnable onStarted) {
Daniel Sandler6ed3fca2012-11-08 09:58:27 -0500160
John Spurlockdcc96812012-10-25 14:35:19 -0400161 final Context context = getContext();
John Spurlock65cbcbe2012-11-06 11:39:34 -0500162 final Bundle animation = useDefaultAnimations ? null
163 : ActivityOptions.makeCustomAnimation(context, 0, 0).toBundle();
Daniel Sandler6ed3fca2012-11-08 09:58:27 -0500164 launchActivityWithAnimation(intent, showsWhileLocked, animation, worker, onStarted);
165 }
166
167 public void launchActivityWithAnimation(final Intent intent,
168 boolean showsWhileLocked,
169 final Bundle animation,
170 final Handler worker,
171 final Runnable onStarted) {
172
John Spurlockdcc96812012-10-25 14:35:19 -0400173 LockPatternUtils lockPatternUtils = getLockPatternUtils();
174 intent.addFlags(
175 Intent.FLAG_ACTIVITY_NEW_TASK
176 | Intent.FLAG_ACTIVITY_SINGLE_TOP
177 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
178 boolean isSecure = lockPatternUtils.isSecure();
179 if (!isSecure || showsWhileLocked) {
John Spurlock34c4fe52012-11-07 10:12:29 -0500180 if (!isSecure) {
181 dismissKeyguardOnNextActivity();
John Spurlockdcc96812012-10-25 14:35:19 -0400182 }
183 try {
John Spurlockdbe24b72012-11-01 13:01:05 -0400184 if (DEBUG) Log.d(TAG, String.format("Starting activity for intent %s at %s",
185 intent, SystemClock.uptimeMillis()));
John Spurlock57f928f2012-11-02 13:09:25 -0400186 startActivityForCurrentUser(intent, animation, worker, onStarted);
John Spurlockdcc96812012-10-25 14:35:19 -0400187 } catch (ActivityNotFoundException e) {
188 Log.w(TAG, "Activity not found for intent + " + intent.getAction());
189 }
190 } else {
191 // Create a runnable to start the activity and ask the user to enter their
192 // credentials.
193 KeyguardSecurityCallback callback = getCallback();
John Spurlock34c4fe52012-11-07 10:12:29 -0500194 callback.setOnDismissAction(new OnDismissAction() {
John Spurlockdcc96812012-10-25 14:35:19 -0400195 @Override
John Spurlock34c4fe52012-11-07 10:12:29 -0500196 public boolean onDismiss() {
197 dismissKeyguardOnNextActivity();
John Spurlock57f928f2012-11-02 13:09:25 -0400198 startActivityForCurrentUser(intent, animation, worker, onStarted);
John Spurlock34c4fe52012-11-07 10:12:29 -0500199 return true;
John Spurlockdcc96812012-10-25 14:35:19 -0400200 }
201 });
202 callback.dismiss(false);
203 }
204 }
205
John Spurlock34c4fe52012-11-07 10:12:29 -0500206 private void dismissKeyguardOnNextActivity() {
207 try {
208 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
209 } catch (RemoteException e) {
210 Log.w(TAG, "can't dismiss keyguard on launch");
211 }
212 }
213
John Spurlock57f928f2012-11-02 13:09:25 -0400214 private void startActivityForCurrentUser(final Intent intent, final Bundle options,
215 Handler worker, final Runnable onStarted) {
216 final UserHandle user = new UserHandle(UserHandle.USER_CURRENT);
217 if (worker == null || onStarted == null) {
218 getContext().startActivityAsUser(intent, options, user);
219 return;
220 }
221 // if worker + onStarted are supplied, run blocking activity launch call in the background
222 worker.post(new Runnable(){
223 @Override
224 public void run() {
225 try {
226 WaitResult result = ActivityManagerNative.getDefault().startActivityAndWait(
227 null /*caller*/,
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800228 null /*caller pkg*/,
John Spurlock57f928f2012-11-02 13:09:25 -0400229 intent,
230 intent.resolveTypeIfNeeded(getContext().getContentResolver()),
231 null /*resultTo*/,
232 null /*resultWho*/,
233 0 /*requestCode*/,
234 Intent.FLAG_ACTIVITY_NEW_TASK,
235 null /*profileFile*/,
236 null /*profileFd*/,
237 options,
238 user.getIdentifier());
239 if (DEBUG) Log.d(TAG, String.format("waitResult[%s,%s,%s,%s] at %s",
240 result.result, result.thisTime, result.totalTime, result.who,
241 SystemClock.uptimeMillis()));
242 } catch (RemoteException e) {
243 Log.w(TAG, "Error starting activity", e);
244 return;
245 }
246 try {
247 onStarted.run();
248 } catch (Throwable t) {
249 Log.w(TAG, "Error running onStarted callback", t);
250 }
251 }});
252 }
253
John Spurlockdcc96812012-10-25 14:35:19 -0400254 private Intent getCameraIntent() {
255 return getLockPatternUtils().isSecure() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
256 }
257
258 private boolean wouldLaunchResolverActivity(Intent intent) {
259 PackageManager packageManager = getContext().getPackageManager();
260 ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
261 PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
262 List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
263 intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
264 return wouldLaunchResolverActivity(resolved, appList);
265 }
266
267 private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
268 // If the list contains the above resolved activity, then it can't be
269 // ResolverActivity itself.
270 for (int i = 0; i < appList.size(); i++) {
271 ResolveInfo tmp = appList.get(i);
272 if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
273 && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
274 return false;
275 }
276 }
277 return true;
278 }
279}