From 33266b39afb826ee90fa524ef73938775ea0ca05 Mon Sep 17 00:00:00 2001 From: Hajime Morrita Date: Mon, 11 Jan 2021 11:30:31 -0800 Subject: [PATCH] Add "repin" command to pinner service shell command. With this CL, "adb shell cmd pinner repin" reset existing APK pinning based on the current flag values. For now, the only dynamic flag related to this is "pin_camera" DeviceConfig flag. Bug: 176838683 Test: Ran following commands and inspected the output: $ adb shell device_config put runtime_native_boot pin_camera false $ adb shell cmd pinner repin && adb shell dumpsys pinner $ adb shell device_config put runtime_native_boot pin_camera true $ adb shell cmd pinner repin && adb shell dumpsys pinner Also covered a couple of error cases: $ adb shell cmd pinner $ adb shell cmd pinner nosuch Change-Id: I3229879b8637a8e0760cffb7292248e97842042f --- .../com/android/server/PinnerService.java | 154 ++++++++++++++---- 1 file changed, 126 insertions(+), 28 deletions(-) diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index a3bcbbe25e886..ce8863cc99939 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -43,6 +43,8 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -61,7 +63,6 @@ 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 com.android.server.SystemService.TargetUser; import com.android.server.wm.ActivityTaskManagerInternal; import dalvik.system.DexFile; @@ -70,6 +71,7 @@ import dalvik.system.VMRuntime; import java.io.Closeable; import java.io.DataInputStream; import java.io.FileDescriptor; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; @@ -100,12 +102,6 @@ public final class PinnerService extends SystemService { private static final int KEY_HOME = 1; private static final int KEY_ASSISTANT = 2; - // Pin the camera application. Default to the system property only if the experiment phenotype - // property is not set. - private static boolean PROP_PIN_CAMERA = - DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, - "pin_camera", - SystemProperties.getBoolean("pinner.pin_camera", false)); // Pin using pinlist.meta when pinning apps. private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean( "pinner.use_pinlist", true); @@ -150,7 +146,13 @@ public final class PinnerService extends SystemService { /** * A set of {@link AppKey} that are configured to be pinned. */ - private final ArraySet mPinKeys = new ArraySet<>(); + @GuardedBy("this") + private ArraySet mPinKeys; + + // Resource-configured pinner flags; + private final boolean mConfiguredToPinCamera; + private final boolean mConfiguredToPinHome; + private final boolean mConfiguredToPinAssistant; private BinderService mBinderService; private PinnerHandler mPinnerHandler = null; @@ -173,25 +175,13 @@ public final class PinnerService extends SystemService { super(context); mContext = context; - boolean shouldPinCamera = context.getResources().getBoolean( + mConfiguredToPinCamera = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerCameraApp); - boolean shouldPinHome = context.getResources().getBoolean( + mConfiguredToPinHome = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerHomeApp); - boolean shouldPinAssistant = context.getResources().getBoolean( + mConfiguredToPinAssistant = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerAssistantApp); - if (shouldPinCamera) { - if (PROP_PIN_CAMERA) { - mPinKeys.add(KEY_CAMERA); - } else if (DEBUG) { - Slog.i(TAG, "Pinner - skip pinning camera app"); - } - } - if (shouldPinHome) { - mPinKeys.add(KEY_HOME); - } - if (shouldPinAssistant) { - mPinKeys.add(KEY_ASSISTANT); - } + mPinKeys = createPinKeys(); mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper()); mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); @@ -259,9 +249,10 @@ public final class PinnerService extends SystemService { * The other files pinned in onStart will not need to be updated. */ public void update(ArraySet updatedPackages, boolean force) { + ArraySet pinKeys = getPinKeys(); int currentUser = ActivityManager.getCurrentUser(); - for (int i = mPinKeys.size() - 1; i >= 0; i--) { - int key = mPinKeys.valueAt(i); + for (int i = pinKeys.size() - 1; i >= 0; i--) { + int key = pinKeys.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); @@ -385,6 +376,14 @@ public final class PinnerService extends SystemService { } } + private void unpinApps() { + ArraySet pinKeys = getPinKeys(); + for (int i = pinKeys.size() - 1; i >= 0; i--) { + int key = pinKeys.valueAt(i); + unpinApp(key); + } + } + private void unpinApp(@AppKey int key) { ArrayList pinnedAppFiles; synchronized (this) { @@ -490,9 +489,72 @@ public final class PinnerService extends SystemService { userHandle)); } + private void sendPinAppsWithUpdatedKeysMessage(int userHandle) { + mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinAppsWithUpdatedKeys, + this, userHandle)); + } + private void sendUnpinAppsMessage() { + mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::unpinApps, this)); + } + + private ArraySet createPinKeys() { + ArraySet pinKeys = new ArraySet<>(); + // Pin the camera application. Default to the system property only if the experiment + // phenotype property is not set. + boolean shouldPinCamera = mConfiguredToPinCamera + && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + "pin_camera", + SystemProperties.getBoolean("pinner.pin_camera", false)); + if (shouldPinCamera) { + pinKeys.add(KEY_CAMERA); + } else if (DEBUG) { + Slog.i(TAG, "Pinner - skip pinning camera app"); + } + + if (mConfiguredToPinHome) { + pinKeys.add(KEY_HOME); + } + if (mConfiguredToPinAssistant) { + pinKeys.add(KEY_ASSISTANT); + } + + return pinKeys; + } + + private synchronized ArraySet getPinKeys() { + return mPinKeys; + } + private void pinApps(int userHandle) { - for (int i = mPinKeys.size() - 1; i >= 0; i--) { - int key = mPinKeys.valueAt(i); + pinAppsInternal(userHandle, false); + } + + private void pinAppsWithUpdatedKeys(int userHandle) { + pinAppsInternal(userHandle, true); + } + + /** + * @param updateKeys True if the pinned app list has to be updated. This is true only when + * "pinner repin" shell command is requested. + */ + private void pinAppsInternal(int userHandle, boolean updateKeys) { + if (updateKeys) { + ArraySet newKeys = createPinKeys(); + synchronized (this) { + // This code path demands preceding unpinApps() call. + if (!mPinnedApps.isEmpty()) { + Slog.e(TAG, "Attempted to update a list of apps, " + + "but apps were already pinned. Skipping."); + return; + } + + mPinKeys = newKeys; + } + } + + ArraySet currentPinKeys = getPinKeys(); + for (int i = currentPinKeys.size() - 1; i >= 0; i--) { + int key = currentPinKeys.valueAt(i); pinApp(key, userHandle, true /* force */); } } @@ -981,6 +1043,42 @@ public final class PinnerService extends SystemService { } } } + + private void repin() { + sendUnpinAppsMessage(); + // TODO(morrita): Consider supporting non-system user. + sendPinAppsWithUpdatedKeysMessage(UserHandle.USER_SYSTEM); + } + + private void printError(FileDescriptor out, String message) { + PrintWriter writer = new PrintWriter(new FileOutputStream(out)); + writer.println(message); + writer.flush(); + } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + if (args.length < 1) { + printError(out, "Command is not given."); + resultReceiver.send(-1, null); + return; + } + + String command = args[0]; + switch (command) { + case "repin": + repin(); + break; + default: + printError(out, String.format( + "Unknown pinner command: %s. Supported commands: repin", command)); + resultReceiver.send(-1, null); + return; + } + + resultReceiver.send(0, null); + } } private static final class PinnedFile implements AutoCloseable {