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