Also bundles all profiling options into a class. Bug: 17040932 Change-Id: I85d675ee1494bdc7308caffdf94145d27c996e9d
289 lines
12 KiB
Java
289 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.keyguard;
|
|
|
|
import com.android.internal.widget.LockPatternUtils;
|
|
|
|
import android.app.ActivityManagerNative;
|
|
import android.app.ActivityOptions;
|
|
import android.app.IActivityManager.WaitResult;
|
|
import android.appwidget.AppWidgetManager;
|
|
import android.appwidget.AppWidgetProviderInfo;
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.RemoteException;
|
|
import android.os.SystemClock;
|
|
import android.os.UserHandle;
|
|
import android.provider.MediaStore;
|
|
import android.util.Log;
|
|
import android.view.WindowManager;
|
|
import android.view.WindowManagerGlobal;
|
|
|
|
import com.android.keyguard.KeyguardHostView.OnDismissAction;
|
|
|
|
import java.util.List;
|
|
|
|
public abstract class KeyguardActivityLauncher {
|
|
private static final String TAG = KeyguardActivityLauncher.class.getSimpleName();
|
|
private static final boolean DEBUG = KeyguardConstants.DEBUG;
|
|
private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
|
|
private static final Intent SECURE_CAMERA_INTENT =
|
|
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
|
|
.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
|
|
private static final Intent INSECURE_CAMERA_INTENT =
|
|
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
|
|
|
|
abstract Context getContext();
|
|
|
|
abstract LockPatternUtils getLockPatternUtils();
|
|
|
|
abstract void setOnDismissAction(OnDismissAction action);
|
|
|
|
abstract void requestDismissKeyguard();
|
|
|
|
public static class CameraWidgetInfo {
|
|
public String contextPackage;
|
|
public int layoutId;
|
|
}
|
|
|
|
public CameraWidgetInfo getCameraWidgetInfo() {
|
|
CameraWidgetInfo info = new CameraWidgetInfo();
|
|
Intent intent = getCameraIntent();
|
|
PackageManager packageManager = getContext().getPackageManager();
|
|
final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
|
|
intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
|
|
if (appList.size() == 0) {
|
|
if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found");
|
|
return null;
|
|
}
|
|
ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
|
|
PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
|
|
getLockPatternUtils().getCurrentUser());
|
|
if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved);
|
|
if (wouldLaunchResolverActivity(resolved, appList)) {
|
|
if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver");
|
|
return info;
|
|
}
|
|
if (resolved == null || resolved.activityInfo == null) {
|
|
return null;
|
|
}
|
|
if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
|
|
if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found");
|
|
return info;
|
|
}
|
|
int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
|
|
if (layoutId == 0) {
|
|
if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified");
|
|
return info;
|
|
}
|
|
info.contextPackage = resolved.activityInfo.packageName;
|
|
info.layoutId = layoutId;
|
|
return info;
|
|
}
|
|
|
|
public void launchCamera(Handler worker, Runnable onSecureCameraStarted) {
|
|
LockPatternUtils lockPatternUtils = getLockPatternUtils();
|
|
|
|
// Workaround to avoid camera release/acquisition race when resuming face unlock
|
|
// after showing lockscreen camera (bug 11063890).
|
|
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
|
|
updateMonitor.setAlternateUnlockEnabled(false);
|
|
|
|
if (mustLaunchSecurely()) {
|
|
// Launch the secure version of the camera
|
|
if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
|
|
// TODO: Show disambiguation dialog instead.
|
|
// For now, we'll treat this like launching any other app from secure keyguard.
|
|
// When they do, user sees the system's ResolverActivity which lets them choose
|
|
// which secure camera to use.
|
|
launchActivity(SECURE_CAMERA_INTENT, false, false, null, null);
|
|
} else {
|
|
launchActivity(SECURE_CAMERA_INTENT, true, false, worker, onSecureCameraStarted);
|
|
}
|
|
} else {
|
|
// Launch the normal camera
|
|
launchActivity(INSECURE_CAMERA_INTENT, false, false, null, null);
|
|
}
|
|
}
|
|
|
|
private boolean mustLaunchSecurely() {
|
|
LockPatternUtils lockPatternUtils = getLockPatternUtils();
|
|
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
|
|
int currentUser = lockPatternUtils.getCurrentUser();
|
|
return lockPatternUtils.isSecure() && !updateMonitor.getUserHasTrust(currentUser);
|
|
}
|
|
|
|
public void launchWidgetPicker(int appWidgetId) {
|
|
Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK);
|
|
|
|
pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
|
pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false);
|
|
pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER,
|
|
AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
|
|
|
|
Bundle options = new Bundle();
|
|
options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
|
|
AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
|
|
pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
|
|
pickIntent.addFlags(
|
|
Intent.FLAG_ACTIVITY_NEW_TASK
|
|
| Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
| Intent.FLAG_ACTIVITY_CLEAR_TOP
|
|
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
|
|
|
|
launchActivity(pickIntent, false, false, null, null);
|
|
}
|
|
|
|
/**
|
|
* Launches the said intent for the current foreground user.
|
|
*
|
|
* @param intent
|
|
* @param showsWhileLocked true if the activity can be run on top of keyguard.
|
|
* See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
|
|
* @param useDefaultAnimations true if default transitions should be used, else suppressed.
|
|
* @param worker if supplied along with onStarted, used to launch the blocking activity call.
|
|
* @param onStarted if supplied along with worker, called after activity is started.
|
|
*/
|
|
public void launchActivity(final Intent intent,
|
|
boolean showsWhileLocked,
|
|
boolean useDefaultAnimations,
|
|
final Handler worker,
|
|
final Runnable onStarted) {
|
|
|
|
final Context context = getContext();
|
|
final Bundle animation = useDefaultAnimations ? null
|
|
: ActivityOptions.makeCustomAnimation(context, 0, 0).toBundle();
|
|
launchActivityWithAnimation(intent, showsWhileLocked, animation, worker, onStarted);
|
|
}
|
|
|
|
public void launchActivityWithAnimation(final Intent intent,
|
|
boolean showsWhileLocked,
|
|
final Bundle animation,
|
|
final Handler worker,
|
|
final Runnable onStarted) {
|
|
|
|
LockPatternUtils lockPatternUtils = getLockPatternUtils();
|
|
intent.addFlags(
|
|
Intent.FLAG_ACTIVITY_NEW_TASK
|
|
| Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
boolean mustLaunchSecurely = mustLaunchSecurely();
|
|
if (!mustLaunchSecurely || showsWhileLocked) {
|
|
if (!mustLaunchSecurely) {
|
|
dismissKeyguardOnNextActivity();
|
|
}
|
|
try {
|
|
if (DEBUG) Log.d(TAG, String.format("Starting activity for intent %s at %s",
|
|
intent, SystemClock.uptimeMillis()));
|
|
startActivityForCurrentUser(intent, animation, worker, onStarted);
|
|
} catch (ActivityNotFoundException e) {
|
|
Log.w(TAG, "Activity not found for intent + " + intent.getAction());
|
|
}
|
|
} else {
|
|
// Create a runnable to start the activity and ask the user to enter their
|
|
// credentials.
|
|
setOnDismissAction(new OnDismissAction() {
|
|
@Override
|
|
public boolean onDismiss() {
|
|
dismissKeyguardOnNextActivity();
|
|
startActivityForCurrentUser(intent, animation, worker, onStarted);
|
|
return true;
|
|
}
|
|
});
|
|
requestDismissKeyguard();
|
|
}
|
|
}
|
|
|
|
private void dismissKeyguardOnNextActivity() {
|
|
try {
|
|
WindowManagerGlobal.getWindowManagerService().dismissKeyguard();
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, "Error dismissing keyguard", e);
|
|
}
|
|
}
|
|
|
|
private void startActivityForCurrentUser(final Intent intent, final Bundle options,
|
|
Handler worker, final Runnable onStarted) {
|
|
final UserHandle user = new UserHandle(UserHandle.USER_CURRENT);
|
|
if (worker == null || onStarted == null) {
|
|
getContext().startActivityAsUser(intent, options, user);
|
|
return;
|
|
}
|
|
// if worker + onStarted are supplied, run blocking activity launch call in the background
|
|
worker.post(new Runnable(){
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
WaitResult result = ActivityManagerNative.getDefault().startActivityAndWait(
|
|
null /*caller*/,
|
|
null /*caller pkg*/,
|
|
intent,
|
|
intent.resolveTypeIfNeeded(getContext().getContentResolver()),
|
|
null /*resultTo*/,
|
|
null /*resultWho*/,
|
|
0 /*requestCode*/,
|
|
Intent.FLAG_ACTIVITY_NEW_TASK,
|
|
null /*profilerInfo*/,
|
|
options,
|
|
user.getIdentifier());
|
|
if (DEBUG) Log.d(TAG, String.format("waitResult[%s,%s,%s,%s] at %s",
|
|
result.result, result.thisTime, result.totalTime, result.who,
|
|
SystemClock.uptimeMillis()));
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, "Error starting activity", e);
|
|
return;
|
|
}
|
|
try {
|
|
onStarted.run();
|
|
} catch (Throwable t) {
|
|
Log.w(TAG, "Error running onStarted callback", t);
|
|
}
|
|
}});
|
|
}
|
|
|
|
private Intent getCameraIntent() {
|
|
return mustLaunchSecurely() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
|
|
}
|
|
|
|
private boolean wouldLaunchResolverActivity(Intent intent) {
|
|
PackageManager packageManager = getContext().getPackageManager();
|
|
ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
|
|
PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
|
|
List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
|
|
intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
|
|
return wouldLaunchResolverActivity(resolved, appList);
|
|
}
|
|
|
|
private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
|
|
// If the list contains the above resolved activity, then it can't be
|
|
// ResolverActivity itself.
|
|
for (int i = 0; i < appList.size(); i++) {
|
|
ResolveInfo tmp = appList.get(i);
|
|
if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
|
|
&& tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|