|
|
|
|
@@ -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();
|
|
|
|
|
|