Loading core/res/res/values/config.xml +3 −0 Original line number Diff line number Diff line Loading @@ -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> Loading core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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" /> Loading services/core/java/com/android/server/PinnerService.java +236 −22 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -38,79 +49,267 @@ 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> * <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 Loading @@ -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; } } } Loading
core/res/res/values/config.xml +3 −0 Original line number Diff line number Diff line Loading @@ -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> Loading
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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" /> Loading
services/core/java/com/android/server/PinnerService.java +236 −22 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -38,79 +49,267 @@ 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> * <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 Loading @@ -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; } } }