Merge "Pinner Service - Pin Camera app on unlock if requested by the config" into nyc-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e260ba666f
@@ -2493,6 +2493,9 @@
|
||||
<string-array translatable="false" name="config_defaultPinnerServiceFiles">
|
||||
</string-array>
|
||||
|
||||
<!-- True if camera app should be pinned via Pinner Service -->
|
||||
<bool name="config_pinnerCameraApp">false</bool>
|
||||
|
||||
<!-- Component that is the default launcher when demo mode is enabled. -->
|
||||
<string name="config_demoModeLauncherComponent">com.android.retaildemo/.DemoPlayer</string>
|
||||
|
||||
|
||||
@@ -2619,6 +2619,7 @@
|
||||
|
||||
<!-- Pinner Service -->
|
||||
<java-symbol type="array" name="config_defaultPinnerServiceFiles" />
|
||||
<java-symbol type="bool" name="config_pinnerCameraApp" />
|
||||
|
||||
<java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
|
||||
|
||||
|
||||
@@ -17,18 +17,29 @@
|
||||
package com.android.server;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.Intent;
|
||||
import android.util.EventLog;
|
||||
import android.util.Slog;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
import android.system.StructStat;
|
||||
|
||||
import com.android.internal.app.ResolverActivity;
|
||||
|
||||
import dalvik.system.VMRuntime;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -37,80 +48,268 @@ import java.io.PrintWriter;
|
||||
/**
|
||||
* <p>PinnerService pins important files for key processes in memory.</p>
|
||||
* <p>Files to pin are specified in the config_defaultPinnerServiceFiles
|
||||
* overlay. </p>
|
||||
* overlay.</p>
|
||||
* <p>Pin the default camera application if specified in config_pinnerCameraApp.</p>
|
||||
*/
|
||||
public final class PinnerService extends SystemService {
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "PinnerService";
|
||||
|
||||
private final Context mContext;
|
||||
private final ArrayList<String> mPinnedFiles = new ArrayList<String>();
|
||||
private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<PinnedFile>();
|
||||
private final ArrayList<PinnedFile> mPinnedCameraFiles = new ArrayList<PinnedFile>();
|
||||
private final boolean mShouldPinCamera;
|
||||
|
||||
private BinderService mBinderService;
|
||||
|
||||
private final long MAX_CAMERA_PIN_SIZE = 50 * (1 << 20); //50MB max
|
||||
|
||||
|
||||
public PinnerService(Context context) {
|
||||
super(context);
|
||||
|
||||
mContext = context;
|
||||
|
||||
mShouldPinCamera = context.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_pinnerCameraApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
Slog.e(TAG, "Starting PinnerService");
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Starting PinnerService");
|
||||
}
|
||||
mBinderService = new BinderService();
|
||||
publishBinderService("pinner", mBinderService);
|
||||
|
||||
// Files to pin come from the overlay and can be specified per-device config
|
||||
String[] filesToPin = mContext.getResources().getStringArray(
|
||||
com.android.internal.R.array.config_defaultPinnerServiceFiles);
|
||||
// Continue trying to pin remaining files even if there is a failure
|
||||
String[] filesToPin = mContext.getResources().getStringArray(com.android.internal.R.array.config_defaultPinnerServiceFiles);
|
||||
for (int i = 0; i < filesToPin.length; i++){
|
||||
boolean success = pinFile(filesToPin[i], 0, 0);
|
||||
if (success == true) {
|
||||
mPinnedFiles.add(filesToPin[i]);
|
||||
Slog.i(TAG, "Pinned file = " + filesToPin[i]);
|
||||
PinnedFile pf = pinFile(filesToPin[i], 0, 0, 0);
|
||||
if (pf != null) {
|
||||
mPinnedFiles.add(pf);
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Pinned file = " + pf.mFilename);
|
||||
}
|
||||
} else {
|
||||
Slog.e(TAG, "Failed to pin file = " + filesToPin[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mlock length bytes of fileToPin in memory, starting at offset
|
||||
// length == 0 means pin from offset to end of file
|
||||
private boolean pinFile(String fileToPin, long offset, long length) {
|
||||
/**
|
||||
* Pin camera on unlock.
|
||||
* We have to wait for unlock because the user's
|
||||
* preference for camera is not available from PackageManager until after
|
||||
* unlock
|
||||
*/
|
||||
@Override
|
||||
public void onUnlockUser(int userHandle) {
|
||||
handlePin(userHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pin camera on user switch.
|
||||
* If more than one user is using the device
|
||||
* each user may set a different preference for the camera app.
|
||||
* Make sure that user's preference is pinned into memory.
|
||||
*/
|
||||
@Override
|
||||
public void onSwitchUser(int userHandle) {
|
||||
handlePin(userHandle);
|
||||
}
|
||||
|
||||
private void handlePin(int userHandle) {
|
||||
if (mShouldPinCamera) {
|
||||
boolean success = pinCamera(userHandle);
|
||||
if (!success) {
|
||||
//this is not necessarily an error
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Failed to pin camera.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* determine if the camera app is already pinned by comparing the
|
||||
* intent resolution to the pinned files list
|
||||
*/
|
||||
private boolean alreadyPinned(int userHandle) {
|
||||
ApplicationInfo cameraInfo = getCameraInfo(userHandle);
|
||||
if (cameraInfo == null ) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
|
||||
if (mPinnedCameraFiles.get(i).mFilename.equals(cameraInfo.sourceDir)) {
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Camera is already pinned");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void unpinCameraApp() {
|
||||
for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
|
||||
unpinFile(mPinnedCameraFiles.get(i));
|
||||
}
|
||||
mPinnedCameraFiles.clear();
|
||||
}
|
||||
|
||||
private boolean isResolverActivity(ActivityInfo info) {
|
||||
return ResolverActivity.class.getName().equals(info.name);
|
||||
}
|
||||
|
||||
private ApplicationInfo getCameraInfo(int userHandle) {
|
||||
// find the camera via an intent
|
||||
// use INTENT_ACTION_STILL_IMAGE_CAMERA instead of _SECURE. On a
|
||||
// device without a fbe enabled, the _SECURE intent will never get set.
|
||||
Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
ResolveInfo cameraResolveInfo = pm.resolveActivityAsUser(
|
||||
cameraIntent, PackageManager.MATCH_DEFAULT_ONLY, userHandle);
|
||||
if (cameraResolveInfo == null ) {
|
||||
//this is not necessarily an error
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Unable to resolve camera intent");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isResolverActivity(cameraResolveInfo.activityInfo))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return cameraResolveInfo.activityInfo.applicationInfo;
|
||||
}
|
||||
|
||||
private boolean pinCamera(int userHandle){
|
||||
//we may have already pinned a camera app. If we've pinned this
|
||||
//camera app, we're done. otherwise, unpin and pin the new app
|
||||
if (alreadyPinned(userHandle)){
|
||||
return true;
|
||||
}
|
||||
|
||||
ApplicationInfo cameraInfo = getCameraInfo(userHandle);
|
||||
if (cameraInfo == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//unpin after checking that the camera intent has resolved
|
||||
//this prevents us from thrashing when switching users with
|
||||
//FBE enabled, because the intent won't resolve until the unlock
|
||||
unpinCameraApp();
|
||||
|
||||
//pin APK
|
||||
String camAPK = cameraInfo.sourceDir;
|
||||
PinnedFile pf = pinFile(camAPK, 0, 0, MAX_CAMERA_PIN_SIZE);
|
||||
if (pf == null) {
|
||||
Slog.e(TAG, "Failed to pin " + camAPK);
|
||||
return false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Pinned " + pf.mFilename);
|
||||
}
|
||||
mPinnedCameraFiles.add(pf);
|
||||
|
||||
//find the location of the odex based on the location of the APK
|
||||
int lastPeriod = camAPK.lastIndexOf('.');
|
||||
int lastSlash = camAPK.lastIndexOf('/', lastPeriod);
|
||||
String base = camAPK.substring(0, lastSlash);
|
||||
String appName = camAPK.substring(lastSlash + 1, lastPeriod);
|
||||
|
||||
// determine the ABI from either ApplicationInfo or Build
|
||||
String arch = "arm";
|
||||
if (cameraInfo.primaryCpuAbi != null
|
||||
&& VMRuntime.is64BitAbi(cameraInfo.primaryCpuAbi)) {
|
||||
arch = arch + "64";
|
||||
} else {
|
||||
if (VMRuntime.is64BitAbi(Build.SUPPORTED_ABIS[0])) {
|
||||
arch = arch + "64";
|
||||
}
|
||||
}
|
||||
String odex = base + "/oat/" + arch + "/" + appName + ".odex";
|
||||
//not all apps have odex files, so not pinning the odex is not a fatal error
|
||||
pf = pinFile(odex, 0, 0, MAX_CAMERA_PIN_SIZE);
|
||||
if (pf != null) {
|
||||
mPinnedCameraFiles.add(pf);
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Pinned " + pf.mFilename);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** mlock length bytes of fileToPin in memory, starting at offset
|
||||
* length == 0 means pin from offset to end of file
|
||||
* maxSize == 0 means infinite
|
||||
*/
|
||||
private static PinnedFile pinFile(String fileToPin, long offset, long length, long maxSize) {
|
||||
FileDescriptor fd = new FileDescriptor();
|
||||
try {
|
||||
fd = Os.open(fileToPin, OsConstants.O_RDONLY | OsConstants.O_CLOEXEC | OsConstants.O_NOFOLLOW, OsConstants.O_RDONLY);
|
||||
fd = Os.open(fileToPin,
|
||||
OsConstants.O_RDONLY | OsConstants.O_CLOEXEC | OsConstants.O_NOFOLLOW,
|
||||
OsConstants.O_RDONLY);
|
||||
|
||||
StructStat sb = Os.fstat(fd);
|
||||
|
||||
if (offset + length > sb.st_size) {
|
||||
Os.close(fd);
|
||||
return false;
|
||||
Slog.e(TAG, "Failed to pin file " + fileToPin +
|
||||
", request extends beyond end of file. offset + length = "
|
||||
+ (offset + length) + ", file length = " + sb.st_size);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
length = sb.st_size - offset;
|
||||
}
|
||||
|
||||
long address = Os.mmap(0, length, OsConstants.PROT_READ, OsConstants.MAP_PRIVATE, fd, offset);
|
||||
if (maxSize > 0 && length > maxSize) {
|
||||
Slog.e(TAG, "Could not pin file " + fileToPin +
|
||||
", size = " + length + ", maxSize = " + maxSize);
|
||||
Os.close(fd);
|
||||
return null;
|
||||
}
|
||||
|
||||
long address = Os.mmap(0, length, OsConstants.PROT_READ,
|
||||
OsConstants.MAP_PRIVATE, fd, offset);
|
||||
Os.close(fd);
|
||||
|
||||
Os.mlock(address, length);
|
||||
|
||||
return true;
|
||||
return new PinnedFile(address, length, fileToPin);
|
||||
} catch (ErrnoException e) {
|
||||
Slog.e(TAG, "Failed to pin file " + fileToPin + " with error " + e.getMessage());
|
||||
Slog.e(TAG, "Could not pin file " + fileToPin + " with error " + e.getMessage());
|
||||
if(fd.valid()) {
|
||||
try { Os.close(fd); }
|
||||
catch (ErrnoException eClose) {Slog.e(TAG, "Failed to close fd, error = " + eClose.getMessage());}
|
||||
try {
|
||||
Os.close(fd);
|
||||
}
|
||||
catch (ErrnoException eClose) {
|
||||
Slog.e(TAG, "Failed to close fd, error = " + eClose.getMessage());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean unpinFile(PinnedFile pf) {
|
||||
try {
|
||||
Os.munlock(pf.mAddress, pf.mLength);
|
||||
} catch (ErrnoException e) {
|
||||
Slog.e(TAG, "Failed to unpin file " + pf.mFilename + " with error " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Unpinned file " + pf.mFilename );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private final class BinderService extends Binder {
|
||||
@Override
|
||||
@@ -118,8 +317,23 @@ public final class PinnerService extends SystemService {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
|
||||
pw.println("Pinned Files:");
|
||||
for (int i = 0; i < mPinnedFiles.size(); i++) {
|
||||
pw.println(mPinnedFiles.get(i));
|
||||
pw.println(mPinnedFiles.get(i).mFilename);
|
||||
}
|
||||
for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
|
||||
pw.println(mPinnedCameraFiles.get(i).mFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class PinnedFile {
|
||||
long mAddress;
|
||||
long mLength;
|
||||
String mFilename;
|
||||
|
||||
PinnedFile(long address, long length, String filename) {
|
||||
mAddress = address;
|
||||
mLength = length;
|
||||
mFilename = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user