Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +37 −14 Original line number Original line Diff line number Diff line Loading @@ -140,6 +140,7 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.LinkedList; import java.util.List; import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.Collectors; Loading Loading @@ -209,6 +210,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; private static final int[] EMPTY_CHILD_SESSION_ARRAY = {}; private static final int[] EMPTY_CHILD_SESSION_ARRAY = {}; private static final String SYSTEM_DATA_LOADER_PACKAGE = "android"; // TODO: enforce INSTALL_ALLOW_TEST // TODO: enforce INSTALL_ALLOW_TEST // TODO: enforce INSTALL_ALLOW_DOWNGRADE // TODO: enforce INSTALL_ALLOW_DOWNGRADE Loading Loading @@ -555,6 +558,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { params.dataLoaderParams); params.dataLoaderParams); } } } } if (isStreamingInstallation() && this.params.dataLoaderParams.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE) { assertShellOrSystemCalling("System data loaders"); } } } public SessionInfo generateInfo() { public SessionInfo generateInfo() { Loading Loading @@ -770,6 +779,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } } } private void assertShellOrSystemCalling(String operation) { switch (Binder.getCallingUid()) { case android.os.Process.SHELL_UID: case android.os.Process.ROOT_UID: case android.os.Process.SYSTEM_UID: break; default: throw new SecurityException(operation + " only supported from shell or system"); } } private void assertCanWrite(boolean reverseMode) { private void assertCanWrite(boolean reverseMode) { if (isDataLoaderInstallation()) { if (isDataLoaderInstallation()) { throw new IllegalStateException( throw new IllegalStateException( Loading @@ -780,15 +800,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { assertPreparedAndNotSealedLocked("assertCanWrite"); assertPreparedAndNotSealedLocked("assertCanWrite"); } } if (reverseMode) { if (reverseMode) { switch (Binder.getCallingUid()) { assertShellOrSystemCalling("Reverse mode"); case android.os.Process.SHELL_UID: case android.os.Process.ROOT_UID: case android.os.Process.SYSTEM_UID: break; default: throw new SecurityException( "Reverse mode only supported from shell or system"); } } } } } Loading Loading @@ -1025,13 +1037,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); } } private class FileSystemConnector extends IPackageInstallerSessionFileSystemConnector.Stub { private final class FileSystemConnector extends IPackageInstallerSessionFileSystemConnector.Stub { final Set<String> mAddedFiles; FileSystemConnector(List<InstallationFile> addedFiles) { mAddedFiles = addedFiles.stream().map(file -> file.getName()).collect( Collectors.toSet()); } @Override @Override public void writeData(String name, long offsetBytes, long lengthBytes, public void writeData(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd) { ParcelFileDescriptor incomingFd) { if (incomingFd == null) { if (incomingFd == null) { throw new IllegalArgumentException("incomingFd can't be null"); throw new IllegalArgumentException("incomingFd can't be null"); } } if (!mAddedFiles.contains(name)) { throw new SecurityException("File name is not in the list of added files."); } try { try { doWriteInternal(name, offsetBytes, lengthBytes, incomingFd); doWriteInternal(name, offsetBytes, lengthBytes, incomingFd); } catch (IOException e) { } catch (IOException e) { Loading Loading @@ -2467,8 +2490,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; return; } } FileSystemConnector connector = new FileSystemConnector(); List<InstallationFile> addedFiles = mFiles.stream().filter( List<InstallationFile> addedFiles = mFiles.stream().filter( file -> sAddedFilter.accept(new File(file.name))).map( file -> sAddedFilter.accept(new File(file.name))).map( file -> new InstallationFile( file -> new InstallationFile( Loading @@ -2480,6 +2501,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect( 0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect( Collectors.toList()); Collectors.toList()); final FileSystemConnector connector = new FileSystemConnector(addedFiles); DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class); DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class); if (dataLoaderManager == null) { if (dataLoaderManager == null) { throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, Loading Loading @@ -2515,11 +2538,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } }; }; final DataLoaderParams params = this.params.dataLoaderParams; final FileSystemControlParcel control = new FileSystemControlParcel(); final FileSystemControlParcel control = new FileSystemControlParcel(); control.callback = connector; control.callback = connector; final DataLoaderParams params = this.params.dataLoaderParams; Bundle dataLoaderParams = new Bundle(); Bundle dataLoaderParams = new Bundle(); dataLoaderParams.putParcelable("componentName", params.getComponentName()); dataLoaderParams.putParcelable("componentName", params.getComponentName()); dataLoaderParams.putParcelable("control", control); dataLoaderParams.putParcelable("control", control); Loading services/core/java/com/android/server/pm/PackageManagerShellCommand.java +3 −9 Original line number Original line Diff line number Diff line Loading @@ -34,7 +34,6 @@ import android.content.IIntentSender; import android.content.Intent; import android.content.Intent; import android.content.IntentSender; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo; import android.content.pm.DataLoaderParams; import android.content.pm.FeatureInfo; import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageInstaller; import android.content.pm.IPackageInstaller; Loading Loading @@ -137,10 +136,6 @@ class PackageManagerShellCommand extends ShellCommand { private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/"; private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/"; private static final int DEFAULT_WAIT_MS = 60 * 1000; private static final int DEFAULT_WAIT_MS = 60 * 1000; private static final String DATA_LOADER_PACKAGE = "android"; private static final String DATA_LOADER_CLASS = "com.android.server.pm.PackageManagerShellCommandDataLoader"; final IPackageManager mInterface; final IPackageManager mInterface; final IPermissionManager mPermissionManager; final IPermissionManager mPermissionManager; final private WeakHashMap<String, Resources> mResourceCache = final private WeakHashMap<String, Resources> mResourceCache = Loading Loading @@ -1163,9 +1158,8 @@ class PackageManagerShellCommand extends ShellCommand { private int runStreamingInstall() throws RemoteException { private int runStreamingInstall() throws RemoteException { final InstallParams params = makeInstallParams(); final InstallParams params = makeInstallParams(); if (params.sessionParams.dataLoaderParams == null) { if (params.sessionParams.dataLoaderParams == null) { final DataLoaderParams dataLoaderParams = DataLoaderParams.forStreaming( params.sessionParams.setDataLoaderParams( new ComponentName(DATA_LOADER_PACKAGE, DATA_LOADER_CLASS), ""); PackageManagerShellCommandDataLoader.getDataLoaderParams(this)); params.sessionParams.setDataLoaderParams(dataLoaderParams); } } return doRunInstall(params); return doRunInstall(params); } } Loading services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java +88 −16 Original line number Original line Diff line number Diff line Loading @@ -17,18 +17,22 @@ package com.android.server.pm; package com.android.server.pm; import android.annotation.NonNull; import android.annotation.NonNull; import android.content.ComponentName; import android.content.pm.DataLoaderParams; import android.content.pm.DataLoaderParams; import android.content.pm.InstallationFile; import android.content.pm.InstallationFile; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor; import android.os.ShellCommand; import android.service.dataloader.DataLoaderService; import android.service.dataloader.DataLoaderService; import android.text.TextUtils; import android.text.TextUtils; import android.util.Slog; import android.util.Slog; import android.util.SparseArray; import libcore.io.IoUtils; import libcore.io.IoUtils; import java.io.File; import java.io.IOException; import java.io.IOException; import java.lang.ref.WeakReference; import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.Collection; import java.util.Collection; /** /** Loading @@ -37,41 +41,109 @@ import java.util.Collection; public class PackageManagerShellCommandDataLoader extends DataLoaderService { public class PackageManagerShellCommandDataLoader extends DataLoaderService { public static final String TAG = "PackageManagerShellCommandDataLoader"; public static final String TAG = "PackageManagerShellCommandDataLoader"; static class DataLoader implements DataLoaderService.DataLoader { private static final String PACKAGE = "android"; private ParcelFileDescriptor mInFd = null; private static final String CLASS = PackageManagerShellCommandDataLoader.class.getName(); private FileSystemConnector mConnector = null; static final SecureRandom sRandom = new SecureRandom(); static final SparseArray<WeakReference<ShellCommand>> sShellCommands = new SparseArray<>(); private static final char ARGS_DELIM = '&'; private static final String SHELL_COMMAND_ID_PREFIX = "shellCommandId="; private static final int INVALID_SHELL_COMMAND_ID = -1; private static final int TOO_MANY_PENDING_SHELL_COMMANDS = 10; private static final String STDIN_PATH = "-"; private static final String STDIN_PATH = "-"; static DataLoaderParams getDataLoaderParams(ShellCommand shellCommand) { int commandId; synchronized (sShellCommands) { // Clean up old references. for (int i = sShellCommands.size() - 1; i >= 0; i--) { WeakReference<ShellCommand> oldRef = sShellCommands.valueAt(i); if (oldRef.get() == null) { sShellCommands.removeAt(i); } } // Sanity check. if (sShellCommands.size() > TOO_MANY_PENDING_SHELL_COMMANDS) { Slog.e(TAG, "Too many pending shell commands: " + sShellCommands.size()); } // Generate new id and put ref to the array. do { commandId = sRandom.nextInt(Integer.MAX_VALUE - 1) + 1; } while (sShellCommands.contains(commandId)); sShellCommands.put(commandId, new WeakReference<>(shellCommand)); } final String args = SHELL_COMMAND_ID_PREFIX + commandId; return DataLoaderParams.forStreaming(new ComponentName(PACKAGE, CLASS), args); } private static int extractShellCommandId(String args) { int sessionIdIdx = args.indexOf(SHELL_COMMAND_ID_PREFIX); if (sessionIdIdx < 0) { Slog.e(TAG, "Missing shell command id param."); return INVALID_SHELL_COMMAND_ID; } sessionIdIdx += SHELL_COMMAND_ID_PREFIX.length(); int delimIdx = args.indexOf(ARGS_DELIM, sessionIdIdx); try { if (delimIdx < 0) { return Integer.parseInt(args.substring(sessionIdIdx)); } else { return Integer.parseInt(args.substring(sessionIdIdx, delimIdx)); } } catch (NumberFormatException e) { Slog.e(TAG, "Incorrect shell command id format.", e); return INVALID_SHELL_COMMAND_ID; } } static class DataLoader implements DataLoaderService.DataLoader { private DataLoaderParams mParams = null; private FileSystemConnector mConnector = null; @Override @Override public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams, public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams, @NonNull FileSystemConnector connector) { @NonNull FileSystemConnector connector) { mParams = dataLoaderParams; mConnector = connector; mConnector = connector; return true; return true; } } @Override @Override public boolean onPrepareImage(Collection<InstallationFile> addedFiles, public boolean onPrepareImage(Collection<InstallationFile> addedFiles, Collection<String> removedFiles) { Collection<String> removedFiles) { final int commandId = extractShellCommandId(mParams.getArguments()); if (commandId == INVALID_SHELL_COMMAND_ID) { return false; } final WeakReference<ShellCommand> shellCommandRef; synchronized (sShellCommands) { shellCommandRef = sShellCommands.get(commandId, null); } final ShellCommand shellCommand = shellCommandRef != null ? shellCommandRef.get() : null; if (shellCommand == null) { Slog.e(TAG, "Missing shell command."); return false; } try { try { for (InstallationFile fileInfo : addedFiles) { for (InstallationFile fileInfo : addedFiles) { String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8); String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8); if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) { if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) { // TODO(b/146080380): add support for STDIN installations. final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup( if (mInFd == null) { shellCommand.getInFileDescriptor()); Slog.e(TAG, "Invalid stdin file descriptor."); return false; } ParcelFileDescriptor inFd = ParcelFileDescriptor.dup( mInFd.getFileDescriptor()); mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd); mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd); } else { } else { File localFile = new File(filePath); ParcelFileDescriptor incomingFd = null; ParcelFileDescriptor incomingFd = null; try { try { // TODO(b/146080380): open files via callback into shell command. incomingFd = shellCommand.openFileForSystem(filePath, "r"); incomingFd = ParcelFileDescriptor.open(localFile, mConnector.writeData(fileInfo.getName(), 0, incomingFd.getStatSize(), ParcelFileDescriptor.MODE_READ_ONLY); mConnector.writeData(fileInfo.getName(), 0, localFile.length(), incomingFd); incomingFd); } finally { } finally { IoUtils.closeQuietly(incomingFd); IoUtils.closeQuietly(incomingFd); Loading Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +37 −14 Original line number Original line Diff line number Diff line Loading @@ -140,6 +140,7 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.LinkedList; import java.util.List; import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.Collectors; Loading Loading @@ -209,6 +210,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; private static final int[] EMPTY_CHILD_SESSION_ARRAY = {}; private static final int[] EMPTY_CHILD_SESSION_ARRAY = {}; private static final String SYSTEM_DATA_LOADER_PACKAGE = "android"; // TODO: enforce INSTALL_ALLOW_TEST // TODO: enforce INSTALL_ALLOW_TEST // TODO: enforce INSTALL_ALLOW_DOWNGRADE // TODO: enforce INSTALL_ALLOW_DOWNGRADE Loading Loading @@ -555,6 +558,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { params.dataLoaderParams); params.dataLoaderParams); } } } } if (isStreamingInstallation() && this.params.dataLoaderParams.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE) { assertShellOrSystemCalling("System data loaders"); } } } public SessionInfo generateInfo() { public SessionInfo generateInfo() { Loading Loading @@ -770,6 +779,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } } } private void assertShellOrSystemCalling(String operation) { switch (Binder.getCallingUid()) { case android.os.Process.SHELL_UID: case android.os.Process.ROOT_UID: case android.os.Process.SYSTEM_UID: break; default: throw new SecurityException(operation + " only supported from shell or system"); } } private void assertCanWrite(boolean reverseMode) { private void assertCanWrite(boolean reverseMode) { if (isDataLoaderInstallation()) { if (isDataLoaderInstallation()) { throw new IllegalStateException( throw new IllegalStateException( Loading @@ -780,15 +800,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { assertPreparedAndNotSealedLocked("assertCanWrite"); assertPreparedAndNotSealedLocked("assertCanWrite"); } } if (reverseMode) { if (reverseMode) { switch (Binder.getCallingUid()) { assertShellOrSystemCalling("Reverse mode"); case android.os.Process.SHELL_UID: case android.os.Process.ROOT_UID: case android.os.Process.SYSTEM_UID: break; default: throw new SecurityException( "Reverse mode only supported from shell or system"); } } } } } Loading Loading @@ -1025,13 +1037,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); } } private class FileSystemConnector extends IPackageInstallerSessionFileSystemConnector.Stub { private final class FileSystemConnector extends IPackageInstallerSessionFileSystemConnector.Stub { final Set<String> mAddedFiles; FileSystemConnector(List<InstallationFile> addedFiles) { mAddedFiles = addedFiles.stream().map(file -> file.getName()).collect( Collectors.toSet()); } @Override @Override public void writeData(String name, long offsetBytes, long lengthBytes, public void writeData(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd) { ParcelFileDescriptor incomingFd) { if (incomingFd == null) { if (incomingFd == null) { throw new IllegalArgumentException("incomingFd can't be null"); throw new IllegalArgumentException("incomingFd can't be null"); } } if (!mAddedFiles.contains(name)) { throw new SecurityException("File name is not in the list of added files."); } try { try { doWriteInternal(name, offsetBytes, lengthBytes, incomingFd); doWriteInternal(name, offsetBytes, lengthBytes, incomingFd); } catch (IOException e) { } catch (IOException e) { Loading Loading @@ -2467,8 +2490,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; return; } } FileSystemConnector connector = new FileSystemConnector(); List<InstallationFile> addedFiles = mFiles.stream().filter( List<InstallationFile> addedFiles = mFiles.stream().filter( file -> sAddedFilter.accept(new File(file.name))).map( file -> sAddedFilter.accept(new File(file.name))).map( file -> new InstallationFile( file -> new InstallationFile( Loading @@ -2480,6 +2501,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect( 0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect( Collectors.toList()); Collectors.toList()); final FileSystemConnector connector = new FileSystemConnector(addedFiles); DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class); DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class); if (dataLoaderManager == null) { if (dataLoaderManager == null) { throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, Loading Loading @@ -2515,11 +2538,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } }; }; final DataLoaderParams params = this.params.dataLoaderParams; final FileSystemControlParcel control = new FileSystemControlParcel(); final FileSystemControlParcel control = new FileSystemControlParcel(); control.callback = connector; control.callback = connector; final DataLoaderParams params = this.params.dataLoaderParams; Bundle dataLoaderParams = new Bundle(); Bundle dataLoaderParams = new Bundle(); dataLoaderParams.putParcelable("componentName", params.getComponentName()); dataLoaderParams.putParcelable("componentName", params.getComponentName()); dataLoaderParams.putParcelable("control", control); dataLoaderParams.putParcelable("control", control); Loading
services/core/java/com/android/server/pm/PackageManagerShellCommand.java +3 −9 Original line number Original line Diff line number Diff line Loading @@ -34,7 +34,6 @@ import android.content.IIntentSender; import android.content.Intent; import android.content.Intent; import android.content.IntentSender; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo; import android.content.pm.DataLoaderParams; import android.content.pm.FeatureInfo; import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageInstaller; import android.content.pm.IPackageInstaller; Loading Loading @@ -137,10 +136,6 @@ class PackageManagerShellCommand extends ShellCommand { private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/"; private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/"; private static final int DEFAULT_WAIT_MS = 60 * 1000; private static final int DEFAULT_WAIT_MS = 60 * 1000; private static final String DATA_LOADER_PACKAGE = "android"; private static final String DATA_LOADER_CLASS = "com.android.server.pm.PackageManagerShellCommandDataLoader"; final IPackageManager mInterface; final IPackageManager mInterface; final IPermissionManager mPermissionManager; final IPermissionManager mPermissionManager; final private WeakHashMap<String, Resources> mResourceCache = final private WeakHashMap<String, Resources> mResourceCache = Loading Loading @@ -1163,9 +1158,8 @@ class PackageManagerShellCommand extends ShellCommand { private int runStreamingInstall() throws RemoteException { private int runStreamingInstall() throws RemoteException { final InstallParams params = makeInstallParams(); final InstallParams params = makeInstallParams(); if (params.sessionParams.dataLoaderParams == null) { if (params.sessionParams.dataLoaderParams == null) { final DataLoaderParams dataLoaderParams = DataLoaderParams.forStreaming( params.sessionParams.setDataLoaderParams( new ComponentName(DATA_LOADER_PACKAGE, DATA_LOADER_CLASS), ""); PackageManagerShellCommandDataLoader.getDataLoaderParams(this)); params.sessionParams.setDataLoaderParams(dataLoaderParams); } } return doRunInstall(params); return doRunInstall(params); } } Loading
services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java +88 −16 Original line number Original line Diff line number Diff line Loading @@ -17,18 +17,22 @@ package com.android.server.pm; package com.android.server.pm; import android.annotation.NonNull; import android.annotation.NonNull; import android.content.ComponentName; import android.content.pm.DataLoaderParams; import android.content.pm.DataLoaderParams; import android.content.pm.InstallationFile; import android.content.pm.InstallationFile; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor; import android.os.ShellCommand; import android.service.dataloader.DataLoaderService; import android.service.dataloader.DataLoaderService; import android.text.TextUtils; import android.text.TextUtils; import android.util.Slog; import android.util.Slog; import android.util.SparseArray; import libcore.io.IoUtils; import libcore.io.IoUtils; import java.io.File; import java.io.IOException; import java.io.IOException; import java.lang.ref.WeakReference; import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.Collection; import java.util.Collection; /** /** Loading @@ -37,41 +41,109 @@ import java.util.Collection; public class PackageManagerShellCommandDataLoader extends DataLoaderService { public class PackageManagerShellCommandDataLoader extends DataLoaderService { public static final String TAG = "PackageManagerShellCommandDataLoader"; public static final String TAG = "PackageManagerShellCommandDataLoader"; static class DataLoader implements DataLoaderService.DataLoader { private static final String PACKAGE = "android"; private ParcelFileDescriptor mInFd = null; private static final String CLASS = PackageManagerShellCommandDataLoader.class.getName(); private FileSystemConnector mConnector = null; static final SecureRandom sRandom = new SecureRandom(); static final SparseArray<WeakReference<ShellCommand>> sShellCommands = new SparseArray<>(); private static final char ARGS_DELIM = '&'; private static final String SHELL_COMMAND_ID_PREFIX = "shellCommandId="; private static final int INVALID_SHELL_COMMAND_ID = -1; private static final int TOO_MANY_PENDING_SHELL_COMMANDS = 10; private static final String STDIN_PATH = "-"; private static final String STDIN_PATH = "-"; static DataLoaderParams getDataLoaderParams(ShellCommand shellCommand) { int commandId; synchronized (sShellCommands) { // Clean up old references. for (int i = sShellCommands.size() - 1; i >= 0; i--) { WeakReference<ShellCommand> oldRef = sShellCommands.valueAt(i); if (oldRef.get() == null) { sShellCommands.removeAt(i); } } // Sanity check. if (sShellCommands.size() > TOO_MANY_PENDING_SHELL_COMMANDS) { Slog.e(TAG, "Too many pending shell commands: " + sShellCommands.size()); } // Generate new id and put ref to the array. do { commandId = sRandom.nextInt(Integer.MAX_VALUE - 1) + 1; } while (sShellCommands.contains(commandId)); sShellCommands.put(commandId, new WeakReference<>(shellCommand)); } final String args = SHELL_COMMAND_ID_PREFIX + commandId; return DataLoaderParams.forStreaming(new ComponentName(PACKAGE, CLASS), args); } private static int extractShellCommandId(String args) { int sessionIdIdx = args.indexOf(SHELL_COMMAND_ID_PREFIX); if (sessionIdIdx < 0) { Slog.e(TAG, "Missing shell command id param."); return INVALID_SHELL_COMMAND_ID; } sessionIdIdx += SHELL_COMMAND_ID_PREFIX.length(); int delimIdx = args.indexOf(ARGS_DELIM, sessionIdIdx); try { if (delimIdx < 0) { return Integer.parseInt(args.substring(sessionIdIdx)); } else { return Integer.parseInt(args.substring(sessionIdIdx, delimIdx)); } } catch (NumberFormatException e) { Slog.e(TAG, "Incorrect shell command id format.", e); return INVALID_SHELL_COMMAND_ID; } } static class DataLoader implements DataLoaderService.DataLoader { private DataLoaderParams mParams = null; private FileSystemConnector mConnector = null; @Override @Override public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams, public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams, @NonNull FileSystemConnector connector) { @NonNull FileSystemConnector connector) { mParams = dataLoaderParams; mConnector = connector; mConnector = connector; return true; return true; } } @Override @Override public boolean onPrepareImage(Collection<InstallationFile> addedFiles, public boolean onPrepareImage(Collection<InstallationFile> addedFiles, Collection<String> removedFiles) { Collection<String> removedFiles) { final int commandId = extractShellCommandId(mParams.getArguments()); if (commandId == INVALID_SHELL_COMMAND_ID) { return false; } final WeakReference<ShellCommand> shellCommandRef; synchronized (sShellCommands) { shellCommandRef = sShellCommands.get(commandId, null); } final ShellCommand shellCommand = shellCommandRef != null ? shellCommandRef.get() : null; if (shellCommand == null) { Slog.e(TAG, "Missing shell command."); return false; } try { try { for (InstallationFile fileInfo : addedFiles) { for (InstallationFile fileInfo : addedFiles) { String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8); String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8); if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) { if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) { // TODO(b/146080380): add support for STDIN installations. final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup( if (mInFd == null) { shellCommand.getInFileDescriptor()); Slog.e(TAG, "Invalid stdin file descriptor."); return false; } ParcelFileDescriptor inFd = ParcelFileDescriptor.dup( mInFd.getFileDescriptor()); mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd); mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd); } else { } else { File localFile = new File(filePath); ParcelFileDescriptor incomingFd = null; ParcelFileDescriptor incomingFd = null; try { try { // TODO(b/146080380): open files via callback into shell command. incomingFd = shellCommand.openFileForSystem(filePath, "r"); incomingFd = ParcelFileDescriptor.open(localFile, mConnector.writeData(fileInfo.getName(), 0, incomingFd.getStatSize(), ParcelFileDescriptor.MODE_READ_ONLY); mConnector.writeData(fileInfo.getName(), 0, localFile.length(), incomingFd); incomingFd); } finally { } finally { IoUtils.closeQuietly(incomingFd); IoUtils.closeQuietly(incomingFd); Loading