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