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 | |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 56 | abstract LockPatternUtils getLockPatternUtils(); |
| 57 | |
Jim Miller | 7751ff6 | 2014-01-14 18:57:03 -0800 | [diff] [blame] | 58 | abstract void setOnDismissAction(OnDismissAction action); |
| 59 | |
| 60 | abstract void requestDismissKeyguard(); |
| 61 | |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 62 | public static class CameraWidgetInfo { |
| 63 | public String contextPackage; |
| 64 | public int layoutId; |
| 65 | } |
| 66 | |
| 67 | public CameraWidgetInfo getCameraWidgetInfo() { |
| 68 | CameraWidgetInfo info = new CameraWidgetInfo(); |
| 69 | Intent intent = getCameraIntent(); |
| 70 | PackageManager packageManager = getContext().getPackageManager(); |
| 71 | final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser( |
| 72 | intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser()); |
| 73 | if (appList.size() == 0) { |
| 74 | if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found"); |
| 75 | return null; |
| 76 | } |
| 77 | ResolveInfo resolved = packageManager.resolveActivityAsUser(intent, |
| 78 | PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, |
| 79 | getLockPatternUtils().getCurrentUser()); |
| 80 | if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved); |
| 81 | if (wouldLaunchResolverActivity(resolved, appList)) { |
| 82 | if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver"); |
| 83 | return info; |
| 84 | } |
| 85 | if (resolved == null || resolved.activityInfo == null) { |
| 86 | return null; |
| 87 | } |
| 88 | if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) { |
| 89 | if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found"); |
| 90 | return info; |
| 91 | } |
| 92 | int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT); |
| 93 | if (layoutId == 0) { |
| 94 | if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified"); |
| 95 | return info; |
| 96 | } |
| 97 | info.contextPackage = resolved.activityInfo.packageName; |
| 98 | info.layoutId = layoutId; |
| 99 | return info; |
| 100 | } |
| 101 | |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 102 | public void launchCamera(Handler worker, Runnable onSecureCameraStarted) { |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 103 | LockPatternUtils lockPatternUtils = getLockPatternUtils(); |
Ruben Brunk | 21a99dd | 2013-11-05 18:29:06 -0800 | [diff] [blame] | 104 | |
| 105 | // Workaround to avoid camera release/acquisition race when resuming face unlock |
| 106 | // after showing lockscreen camera (bug 11063890). |
| 107 | KeyguardUpdateMonitor.getInstance(getContext()).setAlternateUnlockEnabled(false); |
| 108 | |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 109 | if (lockPatternUtils.isSecure()) { |
| 110 | // Launch the secure version of the camera |
| 111 | if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) { |
| 112 | // TODO: Show disambiguation dialog instead. |
| 113 | // For now, we'll treat this like launching any other app from secure keyguard. |
| 114 | // When they do, user sees the system's ResolverActivity which lets them choose |
| 115 | // which secure camera to use. |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 116 | launchActivity(SECURE_CAMERA_INTENT, false, false, null, null); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 117 | } else { |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 118 | launchActivity(SECURE_CAMERA_INTENT, true, false, worker, onSecureCameraStarted); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 119 | } |
| 120 | } else { |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 121 | // Launch the normal camera |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 122 | launchActivity(INSECURE_CAMERA_INTENT, false, false, null, null); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 123 | } |
| 124 | } |
| 125 | |
John Spurlock | 65cbcbe | 2012-11-06 11:39:34 -0500 | [diff] [blame] | 126 | public void launchWidgetPicker(int appWidgetId) { |
| 127 | Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK); |
| 128 | |
| 129 | pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); |
| 130 | pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false); |
| 131 | pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER, |
| 132 | AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD); |
| 133 | |
| 134 | Bundle options = new Bundle(); |
| 135 | options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, |
| 136 | AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD); |
| 137 | pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); |
| 138 | pickIntent.addFlags( |
| 139 | Intent.FLAG_ACTIVITY_NEW_TASK |
| 140 | | Intent.FLAG_ACTIVITY_SINGLE_TOP |
| 141 | | Intent.FLAG_ACTIVITY_CLEAR_TOP |
| 142 | | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); |
| 143 | |
| 144 | launchActivity(pickIntent, false, false, null, null); |
| 145 | } |
| 146 | |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 147 | /** |
| 148 | * Launches the said intent for the current foreground user. |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 149 | * |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 150 | * @param intent |
| 151 | * @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] | 152 | * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED} |
| 153 | * @param useDefaultAnimations true if default transitions should be used, else suppressed. |
| 154 | * @param worker if supplied along with onStarted, used to launch the blocking activity call. |
| 155 | * @param onStarted if supplied along with worker, called after activity is started. |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 156 | */ |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 157 | public void launchActivity(final Intent intent, |
| 158 | boolean showsWhileLocked, |
| 159 | boolean useDefaultAnimations, |
| 160 | final Handler worker, |
| 161 | final Runnable onStarted) { |
Daniel Sandler | 6ed3fca | 2012-11-08 09:58:27 -0500 | [diff] [blame] | 162 | |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 163 | final Context context = getContext(); |
John Spurlock | 65cbcbe | 2012-11-06 11:39:34 -0500 | [diff] [blame] | 164 | final Bundle animation = useDefaultAnimations ? null |
| 165 | : ActivityOptions.makeCustomAnimation(context, 0, 0).toBundle(); |
Daniel Sandler | 6ed3fca | 2012-11-08 09:58:27 -0500 | [diff] [blame] | 166 | launchActivityWithAnimation(intent, showsWhileLocked, animation, worker, onStarted); |
| 167 | } |
| 168 | |
| 169 | public void launchActivityWithAnimation(final Intent intent, |
| 170 | boolean showsWhileLocked, |
| 171 | final Bundle animation, |
| 172 | final Handler worker, |
| 173 | final Runnable onStarted) { |
| 174 | |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 175 | LockPatternUtils lockPatternUtils = getLockPatternUtils(); |
| 176 | intent.addFlags( |
| 177 | Intent.FLAG_ACTIVITY_NEW_TASK |
| 178 | | Intent.FLAG_ACTIVITY_SINGLE_TOP |
| 179 | | Intent.FLAG_ACTIVITY_CLEAR_TOP); |
| 180 | boolean isSecure = lockPatternUtils.isSecure(); |
| 181 | if (!isSecure || showsWhileLocked) { |
John Spurlock | 34c4fe5 | 2012-11-07 10:12:29 -0500 | [diff] [blame] | 182 | if (!isSecure) { |
| 183 | dismissKeyguardOnNextActivity(); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 184 | } |
| 185 | try { |
John Spurlock | dbe24b7 | 2012-11-01 13:01:05 -0400 | [diff] [blame] | 186 | if (DEBUG) Log.d(TAG, String.format("Starting activity for intent %s at %s", |
| 187 | intent, SystemClock.uptimeMillis())); |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 188 | startActivityForCurrentUser(intent, animation, worker, onStarted); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 189 | } catch (ActivityNotFoundException e) { |
| 190 | Log.w(TAG, "Activity not found for intent + " + intent.getAction()); |
| 191 | } |
| 192 | } else { |
| 193 | // Create a runnable to start the activity and ask the user to enter their |
| 194 | // credentials. |
Jim Miller | 7751ff6 | 2014-01-14 18:57:03 -0800 | [diff] [blame] | 195 | setOnDismissAction(new OnDismissAction() { |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 196 | @Override |
John Spurlock | 34c4fe5 | 2012-11-07 10:12:29 -0500 | [diff] [blame] | 197 | public boolean onDismiss() { |
| 198 | dismissKeyguardOnNextActivity(); |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 199 | startActivityForCurrentUser(intent, animation, worker, onStarted); |
John Spurlock | 34c4fe5 | 2012-11-07 10:12:29 -0500 | [diff] [blame] | 200 | return true; |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 201 | } |
| 202 | }); |
Jim Miller | 7751ff6 | 2014-01-14 18:57:03 -0800 | [diff] [blame] | 203 | requestDismissKeyguard(); |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 204 | } |
| 205 | } |
| 206 | |
John Spurlock | 34c4fe5 | 2012-11-07 10:12:29 -0500 | [diff] [blame] | 207 | private void dismissKeyguardOnNextActivity() { |
| 208 | try { |
| 209 | ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); |
| 210 | } catch (RemoteException e) { |
| 211 | Log.w(TAG, "can't dismiss keyguard on launch"); |
| 212 | } |
| 213 | } |
| 214 | |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 215 | private void startActivityForCurrentUser(final Intent intent, final Bundle options, |
| 216 | Handler worker, final Runnable onStarted) { |
| 217 | final UserHandle user = new UserHandle(UserHandle.USER_CURRENT); |
| 218 | if (worker == null || onStarted == null) { |
| 219 | getContext().startActivityAsUser(intent, options, user); |
| 220 | return; |
| 221 | } |
| 222 | // if worker + onStarted are supplied, run blocking activity launch call in the background |
| 223 | worker.post(new Runnable(){ |
| 224 | @Override |
| 225 | public void run() { |
| 226 | try { |
| 227 | WaitResult result = ActivityManagerNative.getDefault().startActivityAndWait( |
| 228 | null /*caller*/, |
Dianne Hackborn | f265ea9 | 2013-01-31 15:00:51 -0800 | [diff] [blame] | 229 | null /*caller pkg*/, |
John Spurlock | 57f928f | 2012-11-02 13:09:25 -0400 | [diff] [blame] | 230 | intent, |
| 231 | intent.resolveTypeIfNeeded(getContext().getContentResolver()), |
| 232 | null /*resultTo*/, |
| 233 | null /*resultWho*/, |
| 234 | 0 /*requestCode*/, |
| 235 | Intent.FLAG_ACTIVITY_NEW_TASK, |
| 236 | null /*profileFile*/, |
| 237 | null /*profileFd*/, |
| 238 | options, |
| 239 | user.getIdentifier()); |
| 240 | if (DEBUG) Log.d(TAG, String.format("waitResult[%s,%s,%s,%s] at %s", |
| 241 | result.result, result.thisTime, result.totalTime, result.who, |
| 242 | SystemClock.uptimeMillis())); |
| 243 | } catch (RemoteException e) { |
| 244 | Log.w(TAG, "Error starting activity", e); |
| 245 | return; |
| 246 | } |
| 247 | try { |
| 248 | onStarted.run(); |
| 249 | } catch (Throwable t) { |
| 250 | Log.w(TAG, "Error running onStarted callback", t); |
| 251 | } |
| 252 | }}); |
| 253 | } |
| 254 | |
John Spurlock | dcc9681 | 2012-10-25 14:35:19 -0400 | [diff] [blame] | 255 | private Intent getCameraIntent() { |
| 256 | return getLockPatternUtils().isSecure() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; |
| 257 | } |
| 258 | |
| 259 | private boolean wouldLaunchResolverActivity(Intent intent) { |
| 260 | PackageManager packageManager = getContext().getPackageManager(); |
| 261 | ResolveInfo resolved = packageManager.resolveActivityAsUser(intent, |
| 262 | PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser()); |
| 263 | List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser( |
| 264 | intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser()); |
| 265 | return wouldLaunchResolverActivity(resolved, appList); |
| 266 | } |
| 267 | |
| 268 | private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) { |
| 269 | // If the list contains the above resolved activity, then it can't be |
| 270 | // ResolverActivity itself. |
| 271 | for (int i = 0; i < appList.size(); i++) { |
| 272 | ResolveInfo tmp = appList.get(i); |
| 273 | if (tmp.activityInfo.name.equals(resolved.activityInfo.name) |
| 274 | && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) { |
| 275 | return false; |
| 276 | } |
| 277 | } |
| 278 | return true; |
| 279 | } |
| 280 | } |