John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 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 | |
Jim Miller | 5ecd811 | 2013-01-09 18:50:26 -0800 | [diff] [blame] | 17 | package com.android.keyguard; |
| 18 | |
| 19 | import com.android.internal.widget.LockPatternUtils; |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 20 | |
| 21 | import android.app.ActivityManagerNative; |
Adam Powell | cfc3086 | 2012-10-29 18:21:31 -0700 | [diff] [blame] | 22 | import android.app.ActivityOptions; |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 23 | import android.app.IActivityManager.WaitResult; |
John Spurlock | 65cbcbe | 2012-11-06 11:39:34 -0500 | [diff] [blame] | 24 | import android.appwidget.AppWidgetManager; |
| 25 | import android.appwidget.AppWidgetProviderInfo; |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 26 | import android.content.ActivityNotFoundException; |
| 27 | import android.content.Context; |
| 28 | import android.content.Intent; |
| 29 | import android.content.pm.PackageManager; |
| 30 | import android.content.pm.ResolveInfo; |
Adam Powell | cfc3086 | 2012-10-29 18:21:31 -0700 | [diff] [blame] | 31 | import android.os.Bundle; |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 32 | import android.os.Handler; |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 33 | import android.os.RemoteException; |
John Spurlock | dbe24b7 | 2012-11-01 13:01:05 -0400 | [diff] [blame] | 34 | import android.os.SystemClock; |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 35 | import android.os.UserHandle; |
| 36 | import android.provider.MediaStore; |
| 37 | import android.util.Log; |
| 38 | import android.view.WindowManager; |
| 39 | |
Jim Miller | 5ecd811 | 2013-01-09 18:50:26 -0800 | [diff] [blame] | 40 | import com.android.keyguard.KeyguardHostView.OnDismissAction; |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 41 | |
| 42 | import java.util.List; |
| 43 | |
| 44 | public 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 Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 100 | public void launchCamera(Handler worker, Runnable onSecureCameraStarted) { |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 101 | LockPatternUtils lockPatternUtils = getLockPatternUtils(); |
Ruben Brunk | 21a99dd | 2013-11-05 18:29:06 -0800 | [diff] [blame] | 102 | |
| 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 Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 107 | 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 Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 114 | launchActivity(SECURE_CAMERA_INTENT, false, false, null, null); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 115 | } else { |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 116 | launchActivity(SECURE_CAMERA_INTENT, true, false, worker, onSecureCameraStarted); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 117 | } |
| 118 | } else { |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 119 | // Launch the normal camera |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 120 | launchActivity(INSECURE_CAMERA_INTENT, false, false, null, null); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 121 | } |
| 122 | } |
| 123 | |
John Spurlock | 65cbcbe | 2012-11-06 11:39:34 -0500 | [diff] [blame] | 124 | 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 Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 145 | /** |
| 146 | * Launches the said intent for the current foreground user. |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 147 | * |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 148 | * @param intent |
| 149 | * @param showsWhileLocked true if the activity can be run on top of keyguard. |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 150 | * 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 Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 154 | */ |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 155 | public void launchActivity(final Intent intent, |
| 156 | boolean showsWhileLocked, |
| 157 | boolean useDefaultAnimations, |
| 158 | final Handler worker, |
| 159 | final Runnable onStarted) { |
Daniel Sandler | 6ed3fca | 2012-11-08 09:58:27 -0500 | [diff] [blame] | 160 | |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 161 | final Context context = getContext(); |
John Spurlock | 65cbcbe | 2012-11-06 11:39:34 -0500 | [diff] [blame] | 162 | final Bundle animation = useDefaultAnimations ? null |
| 163 | : ActivityOptions.makeCustomAnimation(context, 0, 0).toBundle(); |
Daniel Sandler | 6ed3fca | 2012-11-08 09:58:27 -0500 | [diff] [blame] | 164 | 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 Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 173 | 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 Spurlock | 34c4fe5 | 2012-11-07 10:12:29 -0500 | [diff] [blame] | 180 | if (!isSecure) { |
| 181 | dismissKeyguardOnNextActivity(); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 182 | } |
| 183 | try { |
John Spurlock | dbe24b7 | 2012-11-01 13:01:05 -0400 | [diff] [blame] | 184 | if (DEBUG) Log.d(TAG, String.format("Starting activity for intent %s at %s", |
| 185 | intent, SystemClock.uptimeMillis())); |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 186 | startActivityForCurrentUser(intent, animation, worker, onStarted); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 187 | } 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 Spurlock | 34c4fe5 | 2012-11-07 10:12:29 -0500 | [diff] [blame] | 194 | callback.setOnDismissAction(new OnDismissAction() { |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 195 | @Override |
John Spurlock | 34c4fe5 | 2012-11-07 10:12:29 -0500 | [diff] [blame] | 196 | public boolean onDismiss() { |
| 197 | dismissKeyguardOnNextActivity(); |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 198 | startActivityForCurrentUser(intent, animation, worker, onStarted); |
John Spurlock | 34c4fe5 | 2012-11-07 10:12:29 -0500 | [diff] [blame] | 199 | return true; |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 200 | } |
| 201 | }); |
| 202 | callback.dismiss(false); |
| 203 | } |
| 204 | } |
| 205 | |
John Spurlock | 34c4fe5 | 2012-11-07 10:12:29 -0500 | [diff] [blame] | 206 | 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 Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 214 | 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 Hackborn | f265ea9 | 2013-01-31 15:00:51 -0800 | [diff] [blame] | 228 | null /*caller pkg*/, |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 229 | 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 Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 254 | 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 | } |