Merge "Pin compiled code of HOME app" into pi-dev

This commit is contained in:
Jorim Jaggi
2018-07-13 22:36:08 +00:00
committed by Android (Google) Code Review
6 changed files with 369 additions and 104 deletions

View File

@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
@@ -407,4 +408,9 @@ public abstract class ActivityManagerInternal {
* has {@code permission}.
*/
public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func);
/**
* @return The intent used to launch the home activity.
*/
public abstract Intent getHomeIntent();
}

View File

@@ -3175,6 +3175,9 @@
<!-- True if camera app should be pinned via Pinner Service -->
<bool name="config_pinnerCameraApp">false</bool>
<!-- True if home app should be pinned via Pinner Service -->
<bool name="config_pinnerHomeApp">false</bool>
<!-- Number of days preloaded file cache should be preserved on a device before it can be
deleted -->
<integer name="config_keepPreloadsMinDays">7</integer>

View File

@@ -2898,6 +2898,7 @@
<!-- Pinner Service -->
<java-symbol type="array" name="config_defaultPinnerServiceFiles" />
<java-symbol type="bool" name="config_pinnerCameraApp" />
<java-symbol type="bool" name="config_pinnerHomeApp" />
<java-symbol type="string" name="config_doubleTouchGestureEnableFile" />

View File

@@ -16,8 +16,16 @@
package com.android.server;
import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
import static android.app.ActivityManager.UID_OBSERVER_GONE;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -32,18 +40,21 @@ import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ResolverActivity;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
@@ -53,12 +64,12 @@ import java.io.Closeable;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.EOFException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.zip.ZipFile;
import java.util.zip.ZipException;
import java.util.zip.ZipEntry;
/**
@@ -70,16 +81,50 @@ import java.util.zip.ZipEntry;
public final class PinnerService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "PinnerService";
private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); //80MB max
private static final String PIN_META_FILENAME = "pinlist.meta";
private static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE);
private static final int MATCH_FLAGS = PackageManager.MATCH_DEFAULT_ONLY
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
private static final int KEY_CAMERA = 0;
private static final int KEY_HOME = 1;
private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
private static final int MAX_HOME_PIN_SIZE = 6 * (1 << 20); // 6MB max for home app.
@IntDef({KEY_CAMERA, KEY_HOME})
@Retention(RetentionPolicy.SOURCE)
public @interface AppKey {}
private final Context mContext;
private final boolean mShouldPinCamera;
private final ActivityManagerInternal mAmInternal;
private final IActivityManager mAm;
/* These lists protected by PinnerService monitor lock */
private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<PinnedFile>();
private final ArrayList<PinnedFile> mPinnedCameraFiles = new ArrayList<PinnedFile>();
/** The list of the statically pinned files. */
@GuardedBy("this")
private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<>();
/** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
@GuardedBy("this")
private final ArrayMap<Integer, PinnedApp> mPinnedApps = new ArrayMap<>();
/**
* The list of the pinned apps that need to be repinned as soon as the all processes of a given
* uid are no longer active. Note that with background dex opt, the new dex/vdex files are only
* loaded into the processes once it restarts. So in case background dex opt recompiled these
* files, we still need to keep the old ones pinned until the processes restart.
* <p>
* This is a map from uid to {@link AppKey}
*/
@GuardedBy("this")
private final ArrayMap<Integer, Integer> mPendingRepin = new ArrayMap<>();
/**
* A set of {@link AppKey} that are configured to be pinned.
*/
private final ArraySet<Integer> mPinKeys = new ArraySet<>();
private BinderService mBinderService;
private PinnerHandler mPinnerHandler = null;
@@ -87,13 +132,13 @@ public final class PinnerService extends SystemService {
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// If this user's camera app has been updated, update pinned files accordingly.
if (intent.getAction() == Intent.ACTION_PACKAGE_REPLACED) {
// If an app has updated, update pinned files accordingly.
if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
Uri packageUri = intent.getData();
String packageName = packageUri.getSchemeSpecificPart();
ArraySet<String> updatedPackages = new ArraySet<>();
updatedPackages.add(packageName);
update(updatedPackages);
update(updatedPackages, true /* force */);
}
}
};
@@ -102,14 +147,26 @@ public final class PinnerService extends SystemService {
super(context);
mContext = context;
mShouldPinCamera = context.getResources().getBoolean(
boolean shouldPinCamera = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerCameraApp);
boolean shouldPinHome = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerHomeApp);
if (shouldPinCamera) {
mPinKeys.add(KEY_CAMERA);
}
if (shouldPinHome) {
mPinKeys.add(KEY_HOME);
}
mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
mAm = ActivityManager.getService();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addDataScheme("package");
mContext.registerReceiver(mBroadcastReceiver, filter);
registerUidListener();
}
@Override
@@ -122,32 +179,39 @@ public final class PinnerService extends SystemService {
publishLocalService(PinnerService.class, this);
mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG).sendToTarget();
mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, UserHandle.USER_SYSTEM, 0)
.sendToTarget();
sendPinAppsMessage(UserHandle.USER_SYSTEM);
}
/**
* 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.
* Repin apps on user switch.
* <p>
* If more than one user is using the device each user may set a different preference for the
* individual apps. Make sure that user's preference is pinned into memory.
*/
@Override
public void onSwitchUser(int userHandle) {
mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, userHandle, 0).sendToTarget();
sendPinAppsMessage(userHandle);
}
@Override
public void onUnlockUser(int userHandle) {
sendPinAppsMessage(userHandle);
}
/**
* Update the currently pinned files.
* Specifically, this only updates camera pinning.
* Specifically, this only updates pinning for the apps that need to be pinned.
* The other files pinned in onStart will not need to be updated.
*/
public void update(ArraySet<String> updatedPackages) {
ApplicationInfo cameraInfo = getCameraInfo(UserHandle.USER_SYSTEM);
if (cameraInfo != null && updatedPackages.contains(cameraInfo.packageName)) {
Slog.i(TAG, "Updating pinned files.");
mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, UserHandle.USER_SYSTEM, 0)
.sendToTarget();
public void update(ArraySet<String> updatedPackages, boolean force) {
int currentUser = ActivityManager.getCurrentUser();
for (int i = mPinKeys.size() - 1; i >= 0; i--) {
int key = mPinKeys.valueAt(i);
ApplicationInfo info = getInfoForKey(key, currentUser);
if (info != null && updatedPackages.contains(info.packageName)) {
Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force);
sendPinAppMessage(key, currentUser, force);
}
}
}
@@ -174,25 +238,80 @@ public final class PinnerService extends SystemService {
}
}
/**
* Handler for camera pinning message
*/
private void handlePinCamera(int userHandle) {
if (!mShouldPinCamera) return;
if (!pinCamera(userHandle)) {
if (DEBUG) {
Slog.v(TAG, "Failed to pin camera.");
private void registerUidListener() {
try {
mAm.registerUidObserver(new IUidObserver.Stub() {
@Override
public void onUidGone(int uid, boolean disabled) throws RemoteException {
mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
PinnerService::handleUidGone, PinnerService.this, uid));
}
@Override
public void onUidActive(int uid) throws RemoteException {
mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
PinnerService::handleUidActive, PinnerService.this, uid));
}
@Override
public void onUidIdle(int uid, boolean disabled) throws RemoteException {
}
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq)
throws RemoteException {
}
@Override
public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
}
}, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system");
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register uid observer", e);
}
}
private void handleUidGone(int uid) {
updateActiveState(uid, false /* active */);
int key;
synchronized (this) {
// In case we have a pending repin, repin now. See mPendingRepin for more information.
key = mPendingRepin.getOrDefault(uid, -1);
if (key == -1) {
return;
}
mPendingRepin.remove(uid);
}
pinApp(key, ActivityManager.getCurrentUser(), false /* force */);
}
private void handleUidActive(int uid) {
updateActiveState(uid, true /* active */);
}
private void updateActiveState(int uid, boolean active) {
synchronized (this) {
for (int i = mPinnedApps.size() - 1; i >= 0; i--) {
PinnedApp app = mPinnedApps.valueAt(i);
if (app.uid == uid) {
app.active = active;
}
}
}
}
private void unpinCameraApp() {
ArrayList<PinnedFile> pinnedCameraFiles;
private void unpinApp(@AppKey int key) {
ArrayList<PinnedFile> pinnedAppFiles;
synchronized (this) {
pinnedCameraFiles = new ArrayList<>(mPinnedCameraFiles);
mPinnedCameraFiles.clear();
PinnedApp app = mPinnedApps.get(key);
if (app == null) {
return;
}
mPinnedApps.remove(key);
pinnedAppFiles = new ArrayList<>(app.mFiles);
}
for (PinnedFile pinnedFile : pinnedCameraFiles) {
for (PinnedFile pinnedFile : pinnedAppFiles) {
pinnedFile.close();
}
}
@@ -206,64 +325,167 @@ public final class PinnerService extends SystemService {
// 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 | PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userHandle);
if (cameraResolveInfo == null ) {
//this is not necessarily an error
if (DEBUG) {
Slog.v(TAG, "Unable to resolve camera intent");
}
return getApplicationInfoForIntent(cameraIntent, userHandle);
}
private ApplicationInfo getHomeInfo(int userHandle) {
Intent intent = mAmInternal.getHomeIntent();
return getApplicationInfoForIntent(intent, userHandle);
}
private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle) {
if (intent == null) {
return null;
}
if (isResolverActivity(cameraResolveInfo.activityInfo))
{
if (DEBUG) {
Slog.v(TAG, "cameraIntent returned resolverActivity");
}
ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser(intent,
MATCH_FLAGS, userHandle);
if (info == null) {
return null;
}
if (isResolverActivity(info.activityInfo)) {
return null;
}
return info.activityInfo.applicationInfo;
}
return cameraResolveInfo.activityInfo.applicationInfo;
private void sendPinAppsMessage(int userHandle) {
mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApps, this,
userHandle));
}
private void pinApps(int userHandle) {
for (int i = mPinKeys.size() - 1; i >= 0; i--) {
int key = mPinKeys.valueAt(i);
pinApp(key, userHandle, true /* force */);
}
}
/**
* If the camera app is already pinned, unpin and repin it.
* @see #pinApp(int, int, boolean)
*/
private boolean pinCamera(int userHandle){
ApplicationInfo cameraInfo = getCameraInfo(userHandle);
if (cameraInfo == null) {
return false;
private void sendPinAppMessage(int key, int userHandle, boolean force) {
mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApp, this,
key, userHandle, force));
}
/**
* Pins an app of a specific type {@code key}.
*
* @param force If false, this will not repin the app if it's currently active. See
* {@link #mPendingRepin}.
*/
private void pinApp(int key, int userHandle, boolean force) {
int uid = getUidForKey(key);
// In case the app is currently active, don't repin until next process restart. See
// mPendingRepin for more information.
if (!force && uid != -1) {
synchronized (this) {
mPendingRepin.put(uid, key);
}
return;
}
unpinApp(key);
ApplicationInfo info = getInfoForKey(key, userHandle);
if (info != null) {
pinApp(key, info);
}
}
/**
* Checks whether the pinned package with {@code key} is active or not.
* @return The uid of the pinned app, or {@code -1} otherwise.
*/
private int getUidForKey(@AppKey int key) {
synchronized (this) {
PinnedApp existing = mPinnedApps.get(key);
return existing != null && existing.active
? existing.uid
: -1;
}
}
/**
* Retrieves the current application info for the given app type.
*
* @param key The app type to retrieve the info for.
* @param userHandle The user id of the current user.
*/
private @Nullable ApplicationInfo getInfoForKey(@AppKey int key, int userHandle) {
switch (key) {
case KEY_CAMERA:
return getCameraInfo(userHandle);
case KEY_HOME:
return getHomeInfo(userHandle);
default:
return null;
}
}
/**
* @return The app type name for {@code key}.
*/
private String getNameForKey(@AppKey int key) {
switch (key) {
case KEY_CAMERA:
return "Camera";
case KEY_HOME:
return "Home";
default:
return null;
}
}
/**
* @return The maximum amount of bytes to be pinned for an app of type {@code key}.
*/
private int getSizeLimitForKey(@AppKey int key) {
switch (key) {
case KEY_CAMERA:
return MAX_CAMERA_PIN_SIZE;
case KEY_HOME:
return MAX_HOME_PIN_SIZE;
default:
return 0;
}
}
/**
* Pins an application.
*
* @param key The key of the app to pin.
* @param appInfo The corresponding app info.
*/
private void pinApp(@AppKey int key, @Nullable ApplicationInfo appInfo) {
if (appInfo == null) {
return;
}
//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();
PinnedApp pinnedApp = new PinnedApp(appInfo);
synchronized (this) {
mPinnedApps.put(key, pinnedApp);
}
//pin APK
String camAPK = cameraInfo.sourceDir;
PinnedFile pf = pinFile(camAPK,
MAX_CAMERA_PIN_SIZE,
/*attemptPinIntrospection=*/true);
// pin APK
int pinSizeLimit = getSizeLimitForKey(key);
String apk = appInfo.sourceDir;
PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true);
if (pf == null) {
Slog.e(TAG, "Failed to pin " + camAPK);
return false;
Slog.e(TAG, "Failed to pin " + apk);
return;
}
if (DEBUG) {
Slog.i(TAG, "Pinned " + pf.fileName);
}
synchronized (this) {
mPinnedCameraFiles.add(pf);
pinnedApp.mFiles.add(pf);
}
// determine the ABI from either ApplicationInfo or Build
String arch = "arm";
if (cameraInfo.primaryCpuAbi != null) {
if (VMRuntime.is64BitAbi(cameraInfo.primaryCpuAbi)) {
if (appInfo.primaryCpuAbi != null) {
if (VMRuntime.is64BitAbi(appInfo.primaryCpuAbi)) {
arch = arch + "64";
}
} else {
@@ -273,32 +495,29 @@ public final class PinnerService extends SystemService {
}
// get the path to the odex or oat file
String baseCodePath = cameraInfo.getBaseCodePath();
String baseCodePath = appInfo.getBaseCodePath();
String[] files = null;
try {
files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
} catch (IOException ioe) {}
if (files == null) {
return true;
return;
}
//not pinning the oat/odex is not a fatal error
for (String file : files) {
pf = pinFile(file, MAX_CAMERA_PIN_SIZE, /*attemptPinIntrospection=*/false);
pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
if (pf != null) {
synchronized (this) {
mPinnedCameraFiles.add(pf);
pinnedApp.mFiles.add(pf);
}
if (DEBUG) {
Slog.i(TAG, "Pinned " + pf.fileName);
}
}
}
return true;
}
/** mlock length bytes of fileToPin in memory
*
* If attemptPinIntrospection is true, then treat the file to pin as a zip file and
@@ -581,24 +800,38 @@ public final class PinnerService extends SystemService {
}
}
private synchronized ArrayList<PinnedFile> snapshotPinnedFiles() {
int nrPinnedFiles = mPinnedFiles.size() + mPinnedCameraFiles.size();
ArrayList<PinnedFile> pinnedFiles = new ArrayList<>(nrPinnedFiles);
pinnedFiles.addAll(mPinnedFiles);
pinnedFiles.addAll(mPinnedCameraFiles);
return pinnedFiles;
}
private final class BinderService extends Binder {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
long totalSize = 0;
for (PinnedFile pinnedFile : snapshotPinnedFiles()) {
pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
totalSize += pinnedFile.bytesPinned;
synchronized (PinnerService.this) {
long totalSize = 0;
for (PinnedFile pinnedFile : mPinnedFiles) {
pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
totalSize += pinnedFile.bytesPinned;
}
pw.println();
for (int key : mPinnedApps.keySet()) {
PinnedApp app = mPinnedApps.get(key);
pw.print(getNameForKey(key));
pw.print(" uid="); pw.print(app.uid);
pw.print(" active="); pw.print(app.active);
pw.println();
for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
pw.print(" "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
totalSize += pf.bytesPinned;
}
}
pw.format("Total size: %s\n", totalSize);
pw.println();
if (!mPendingRepin.isEmpty()) {
pw.print("Pending repin: ");
for (int key : mPendingRepin.values()) {
pw.print(getNameForKey(key)); pw.print(' ');
}
pw.println();
}
}
pw.format("Total size: %s\n", totalSize);
}
}
@@ -634,8 +867,30 @@ public final class PinnerService extends SystemService {
int length;
}
/**
* Represents an app that was pinned.
*/
private final class PinnedApp {
/**
* The uid of the package being pinned. This stays constant while the package stays
* installed.
*/
final int uid;
/** Whether it is currently active, i.e. there is a running process from that package. */
boolean active;
/** List of pinned files. */
final ArrayList<PinnedFile> mFiles = new ArrayList<>();
private PinnedApp(ApplicationInfo appInfo) {
uid = appInfo.uid;
active = mAmInternal.isUidActive(uid);
}
}
final class PinnerHandler extends Handler {
static final int PIN_CAMERA_MSG = 4000;
static final int PIN_ONSTART_MSG = 4001;
public PinnerHandler(Looper looper) {
@@ -645,13 +900,6 @@ public final class PinnerService extends SystemService {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case PIN_CAMERA_MSG:
{
handlePinCamera(msg.arg1);
}
break;
case PIN_ONSTART_MSG:
{
handlePinOnStart();

View File

@@ -26828,6 +26828,13 @@ public class ActivityManagerService extends IActivityManager.Stub
public void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
ActivityManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func);
}
@Override
public Intent getHomeIntent() {
synchronized (ActivityManagerService.this) {
return ActivityManagerService.this.getHomeIntent();
}
}
}
/**

View File

@@ -489,7 +489,7 @@ public class BackgroundDexOptService extends JobService {
PinnerService pinnerService = LocalServices.getService(PinnerService.class);
if (pinnerService != null) {
Log.i(TAG, "Pinning optimized code " + updatedPackages);
pinnerService.update(updatedPackages);
pinnerService.update(updatedPackages, false /* force */);
}
}