Loading core/java/android/content/pm/IPackageInstallerSession.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ interface IPackageInstallerSession { ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes); ParcelFileDescriptor openRead(String name); void removeSplit(String splitName); void close(); void commit(in IntentSender statusReceiver); void abandon(); Loading core/java/android/content/pm/PackageInstaller.java +21 −0 Original line number Diff line number Diff line Loading @@ -794,6 +794,27 @@ public class PackageInstaller { } } /** * Removes a split. * <p> * Split removals occur prior to adding new APKs. If upgrading a feature * split, it is not expected nor desirable to remove the split prior to * upgrading. * <p> * When split removal is bundled with new APKs, the packageName must be * identical. */ public void removeSplit(@NonNull String splitName) throws IOException { try { mSession.removeSplit(splitName); } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Attempt to commit everything staged in this session. This may require * user intervention, and so it may not happen immediately. The final Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +96 −15 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageInstallerSession; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageInstaller.SessionParams; Loading @@ -58,6 +59,7 @@ import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.system.StructStat; import android.text.TextUtils; import android.util.ArraySet; import android.util.ExceptionUtils; import android.util.MathUtils; Loading @@ -77,6 +79,7 @@ import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapt import java.io.File; import java.io.FileDescriptor; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; Loading @@ -86,6 +89,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String TAG = "PackageInstaller"; private static final boolean LOGD = true; private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed"; private static final int MSG_COMMIT = 0; Loading Loading @@ -171,6 +175,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private File mInheritedFilesBase; private static final FileFilter sAddedFilter = new FileFilter() { @Override public boolean accept(File file) { // Installers can't stage directories, so it's fine to ignore // entries like "lost+found". if (file.isDirectory()) return false; if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; return true; } }; private static final FileFilter sRemovedFilter = new FileFilter() { @Override public boolean accept(File file) { if (file.isDirectory()) return false; if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; return true; } }; private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { Loading Loading @@ -345,6 +368,32 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } @Override public void removeSplit(String splitName) { if (TextUtils.isEmpty(params.appPackageName)) { throw new IllegalStateException("Must specify package name to remove a split"); } try { createRemoveSplitMarker(splitName); } catch (IOException e) { throw ExceptionUtils.wrap(e); } } private void createRemoveSplitMarker(String splitName) throws IOException { try { final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION; if (!FileUtils.isValidExtFilename(markerName)) { throw new IllegalArgumentException("Invalid marker: " + markerName); } final File target = new File(resolveStageDir(), markerName); target.createNewFile(); Os.chmod(target.getAbsolutePath(), 0 /*mode*/); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } } @Override public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { try { Loading Loading @@ -608,22 +657,28 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedStagedFiles.clear(); mResolvedInheritedFiles.clear(); final File[] files = mResolvedStageDir.listFiles(); if (ArrayUtils.isEmpty(files)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter); final List<String> removeSplitList = new ArrayList<>(); if (!ArrayUtils.isEmpty(removedFiles)) { for (File removedFile : removedFiles) { final String fileName = removedFile.getName(); final String splitName = fileName.substring( 0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length()); removeSplitList.add(splitName); } } final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter); if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); } // Verify that all staged packages are internally consistent final ArraySet<String> stagedSplits = new ArraySet<>(); for (File file : files) { // Installers can't stage directories, so it's fine to ignore // entries like "lost+found". if (file.isDirectory()) continue; for (File addedFile : addedFiles) { final ApkLite apk; try { apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES); apk = PackageParser.parseApkLite( addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES); } catch (PackageParserException e) { throw PackageManagerException.from(e); } Loading @@ -642,7 +697,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSignatures = apk.signatures; } assertApkConsistent(String.valueOf(file), apk); assertApkConsistent(String.valueOf(addedFile), apk); // Take this opportunity to enforce uniform naming final String targetName; Loading @@ -657,8 +712,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } final File targetFile = new File(mResolvedStageDir, targetName); if (!file.equals(targetFile)) { file.renameTo(targetFile); if (!addedFile.equals(targetFile)) { addedFile.renameTo(targetFile); } // Base is coming from session Loading @@ -669,6 +724,27 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedStagedFiles.add(targetFile); } if (removeSplitList.size() > 0) { // validate split names marked for removal final int flags = mSignatures == null ? PackageManager.GET_SIGNATURES : 0; final PackageInfo pkg = mPm.getPackageInfo(params.appPackageName, flags, userId); for (String splitName : removeSplitList) { if (!ArrayUtils.contains(pkg.splitNames, splitName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Split not found: " + splitName); } } // ensure we've got appropriate package name, version code and signatures if (mPackageName == null) { mPackageName = pkg.packageName; mVersionCode = pkg.versionCode; } if (mSignatures == null) { mSignatures = pkg.signatures; } } if (params.mode == SessionParams.MODE_FULL_INSTALL) { // Full installs must include a base package if (!stagedSplits.contains(null)) { Loading Loading @@ -707,8 +783,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { for (int i = 0; i < existing.splitNames.length; i++) { final String splitName = existing.splitNames[i]; final File splitFile = new File(existing.splitCodePaths[i]); if (!stagedSplits.contains(splitName)) { final boolean splitRemoved = removeSplitList.contains(splitName); if (!stagedSplits.contains(splitName) && !splitRemoved) { mResolvedInheritedFiles.add(splitFile); } } Loading Loading @@ -748,6 +824,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " + apk.packageName + " inconsistent with " + mPackageName); } if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " specified package " + params.appPackageName + " inconsistent with " + apk.packageName); } if (mVersionCode != apk.versionCode) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " version code " + apk.versionCode + " inconsistent with " Loading services/core/java/com/android/server/pm/PackageManagerShellCommand.java +78 −5 Original line number Diff line number Diff line Loading @@ -95,6 +95,8 @@ class PackageManagerShellCommand extends ShellCommand { return runInstallCommit(); case "install-create": return runInstallCreate(); case "install-remove": return runInstallRemove(); case "install-write": return runInstallWrite(); case "compile": Loading Loading @@ -136,11 +138,12 @@ class PackageManagerShellCommand extends ShellCommand { pw.println("Error: must either specify a package size or an APK file"); return 1; } if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk", if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk", false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } abandonSession = false; Loading Loading @@ -225,7 +228,20 @@ class PackageManagerShellCommand extends ShellCommand { final int sessionId = Integer.parseInt(getNextArg()); final String splitName = getNextArg(); final String path = getNextArg(); return doWriteSession(sessionId, path, sizeBytes, splitName, true /*logSuccess*/); return doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/); } private int runInstallRemove() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); final int sessionId = Integer.parseInt(getNextArg()); final String splitName = getNextArg(); if (splitName == null) { pw.println("Error: split name not specified"); return 1; } return doRemoveSplit(sessionId, splitName, true /*logSuccess*/); } private int runCompile() throws RemoteException { Loading Loading @@ -666,12 +682,18 @@ class PackageManagerShellCommand extends ShellCommand { } } String packageName = getNextArg(); final String packageName = getNextArg(); if (packageName == null) { pw.println("Error: package name not specified"); return 1; } // if a split is specified, just remove it and not the whole package final String splitName = getNextArg(); if (splitName != null) { return runRemoveSplit(packageName, splitName); } userId = translateUserId(userId, "runUninstall"); if (userId == UserHandle.USER_ALL) { userId = UserHandle.USER_SYSTEM; Loading Loading @@ -709,6 +731,36 @@ class PackageManagerShellCommand extends ShellCommand { } } private int runRemoveSplit(String packageName, String splitName) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); final SessionParams sessionParams = new SessionParams(SessionParams.MODE_INHERIT_EXISTING); sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; sessionParams.appPackageName = packageName; final int sessionId = doCreateSession(sessionParams, null /*installerPackageName*/, UserHandle.USER_ALL); boolean abandonSession = true; try { if (doRemoveSplit(sessionId, splitName, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } abandonSession = false; pw.println("Success"); return 0; } finally { if (abandonSession) { try { doAbandonSession(sessionId, false /*logSuccess*/); } catch (Exception ignore) { } } } } private Intent parseIntentAndUser() throws URISyntaxException { mTargetUser = UserHandle.USER_CURRENT; Intent intent = Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() { Loading Loading @@ -948,7 +1000,7 @@ class PackageManagerShellCommand extends ShellCommand { return sessionId; } private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName, private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName, boolean logSuccess) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); if ("-".equals(inPath)) { Loading Loading @@ -1004,6 +1056,27 @@ class PackageManagerShellCommand extends ShellCommand { } } private int doRemoveSplit(int sessionId, String splitName, boolean logSuccess) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); PackageInstaller.Session session = null; try { session = new PackageInstaller.Session( mInterface.getPackageInstaller().openSession(sessionId)); session.removeSplit(splitName); if (logSuccess) { pw.println("Success"); } return 0; } catch (IOException e) { pw.println("Error: failed to remove split; " + e.getMessage()); return 1; } finally { IoUtils.closeQuietly(session); } } private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); PackageInstaller.Session session = null; Loading Loading
core/java/android/content/pm/IPackageInstallerSession.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ interface IPackageInstallerSession { ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes); ParcelFileDescriptor openRead(String name); void removeSplit(String splitName); void close(); void commit(in IntentSender statusReceiver); void abandon(); Loading
core/java/android/content/pm/PackageInstaller.java +21 −0 Original line number Diff line number Diff line Loading @@ -794,6 +794,27 @@ public class PackageInstaller { } } /** * Removes a split. * <p> * Split removals occur prior to adding new APKs. If upgrading a feature * split, it is not expected nor desirable to remove the split prior to * upgrading. * <p> * When split removal is bundled with new APKs, the packageName must be * identical. */ public void removeSplit(@NonNull String splitName) throws IOException { try { mSession.removeSplit(splitName); } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Attempt to commit everything staged in this session. This may require * user intervention, and so it may not happen immediately. The final Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +96 −15 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageInstallerSession; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageInstaller.SessionParams; Loading @@ -58,6 +59,7 @@ import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.system.StructStat; import android.text.TextUtils; import android.util.ArraySet; import android.util.ExceptionUtils; import android.util.MathUtils; Loading @@ -77,6 +79,7 @@ import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapt import java.io.File; import java.io.FileDescriptor; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; Loading @@ -86,6 +89,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String TAG = "PackageInstaller"; private static final boolean LOGD = true; private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed"; private static final int MSG_COMMIT = 0; Loading Loading @@ -171,6 +175,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private File mInheritedFilesBase; private static final FileFilter sAddedFilter = new FileFilter() { @Override public boolean accept(File file) { // Installers can't stage directories, so it's fine to ignore // entries like "lost+found". if (file.isDirectory()) return false; if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; return true; } }; private static final FileFilter sRemovedFilter = new FileFilter() { @Override public boolean accept(File file) { if (file.isDirectory()) return false; if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; return true; } }; private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { Loading Loading @@ -345,6 +368,32 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } @Override public void removeSplit(String splitName) { if (TextUtils.isEmpty(params.appPackageName)) { throw new IllegalStateException("Must specify package name to remove a split"); } try { createRemoveSplitMarker(splitName); } catch (IOException e) { throw ExceptionUtils.wrap(e); } } private void createRemoveSplitMarker(String splitName) throws IOException { try { final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION; if (!FileUtils.isValidExtFilename(markerName)) { throw new IllegalArgumentException("Invalid marker: " + markerName); } final File target = new File(resolveStageDir(), markerName); target.createNewFile(); Os.chmod(target.getAbsolutePath(), 0 /*mode*/); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } } @Override public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { try { Loading Loading @@ -608,22 +657,28 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedStagedFiles.clear(); mResolvedInheritedFiles.clear(); final File[] files = mResolvedStageDir.listFiles(); if (ArrayUtils.isEmpty(files)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter); final List<String> removeSplitList = new ArrayList<>(); if (!ArrayUtils.isEmpty(removedFiles)) { for (File removedFile : removedFiles) { final String fileName = removedFile.getName(); final String splitName = fileName.substring( 0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length()); removeSplitList.add(splitName); } } final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter); if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); } // Verify that all staged packages are internally consistent final ArraySet<String> stagedSplits = new ArraySet<>(); for (File file : files) { // Installers can't stage directories, so it's fine to ignore // entries like "lost+found". if (file.isDirectory()) continue; for (File addedFile : addedFiles) { final ApkLite apk; try { apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES); apk = PackageParser.parseApkLite( addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES); } catch (PackageParserException e) { throw PackageManagerException.from(e); } Loading @@ -642,7 +697,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSignatures = apk.signatures; } assertApkConsistent(String.valueOf(file), apk); assertApkConsistent(String.valueOf(addedFile), apk); // Take this opportunity to enforce uniform naming final String targetName; Loading @@ -657,8 +712,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } final File targetFile = new File(mResolvedStageDir, targetName); if (!file.equals(targetFile)) { file.renameTo(targetFile); if (!addedFile.equals(targetFile)) { addedFile.renameTo(targetFile); } // Base is coming from session Loading @@ -669,6 +724,27 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedStagedFiles.add(targetFile); } if (removeSplitList.size() > 0) { // validate split names marked for removal final int flags = mSignatures == null ? PackageManager.GET_SIGNATURES : 0; final PackageInfo pkg = mPm.getPackageInfo(params.appPackageName, flags, userId); for (String splitName : removeSplitList) { if (!ArrayUtils.contains(pkg.splitNames, splitName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Split not found: " + splitName); } } // ensure we've got appropriate package name, version code and signatures if (mPackageName == null) { mPackageName = pkg.packageName; mVersionCode = pkg.versionCode; } if (mSignatures == null) { mSignatures = pkg.signatures; } } if (params.mode == SessionParams.MODE_FULL_INSTALL) { // Full installs must include a base package if (!stagedSplits.contains(null)) { Loading Loading @@ -707,8 +783,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { for (int i = 0; i < existing.splitNames.length; i++) { final String splitName = existing.splitNames[i]; final File splitFile = new File(existing.splitCodePaths[i]); if (!stagedSplits.contains(splitName)) { final boolean splitRemoved = removeSplitList.contains(splitName); if (!stagedSplits.contains(splitName) && !splitRemoved) { mResolvedInheritedFiles.add(splitFile); } } Loading Loading @@ -748,6 +824,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " + apk.packageName + " inconsistent with " + mPackageName); } if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " specified package " + params.appPackageName + " inconsistent with " + apk.packageName); } if (mVersionCode != apk.versionCode) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " version code " + apk.versionCode + " inconsistent with " Loading
services/core/java/com/android/server/pm/PackageManagerShellCommand.java +78 −5 Original line number Diff line number Diff line Loading @@ -95,6 +95,8 @@ class PackageManagerShellCommand extends ShellCommand { return runInstallCommit(); case "install-create": return runInstallCreate(); case "install-remove": return runInstallRemove(); case "install-write": return runInstallWrite(); case "compile": Loading Loading @@ -136,11 +138,12 @@ class PackageManagerShellCommand extends ShellCommand { pw.println("Error: must either specify a package size or an APK file"); return 1; } if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk", if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk", false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } abandonSession = false; Loading Loading @@ -225,7 +228,20 @@ class PackageManagerShellCommand extends ShellCommand { final int sessionId = Integer.parseInt(getNextArg()); final String splitName = getNextArg(); final String path = getNextArg(); return doWriteSession(sessionId, path, sizeBytes, splitName, true /*logSuccess*/); return doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/); } private int runInstallRemove() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); final int sessionId = Integer.parseInt(getNextArg()); final String splitName = getNextArg(); if (splitName == null) { pw.println("Error: split name not specified"); return 1; } return doRemoveSplit(sessionId, splitName, true /*logSuccess*/); } private int runCompile() throws RemoteException { Loading Loading @@ -666,12 +682,18 @@ class PackageManagerShellCommand extends ShellCommand { } } String packageName = getNextArg(); final String packageName = getNextArg(); if (packageName == null) { pw.println("Error: package name not specified"); return 1; } // if a split is specified, just remove it and not the whole package final String splitName = getNextArg(); if (splitName != null) { return runRemoveSplit(packageName, splitName); } userId = translateUserId(userId, "runUninstall"); if (userId == UserHandle.USER_ALL) { userId = UserHandle.USER_SYSTEM; Loading Loading @@ -709,6 +731,36 @@ class PackageManagerShellCommand extends ShellCommand { } } private int runRemoveSplit(String packageName, String splitName) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); final SessionParams sessionParams = new SessionParams(SessionParams.MODE_INHERIT_EXISTING); sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; sessionParams.appPackageName = packageName; final int sessionId = doCreateSession(sessionParams, null /*installerPackageName*/, UserHandle.USER_ALL); boolean abandonSession = true; try { if (doRemoveSplit(sessionId, splitName, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } abandonSession = false; pw.println("Success"); return 0; } finally { if (abandonSession) { try { doAbandonSession(sessionId, false /*logSuccess*/); } catch (Exception ignore) { } } } } private Intent parseIntentAndUser() throws URISyntaxException { mTargetUser = UserHandle.USER_CURRENT; Intent intent = Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() { Loading Loading @@ -948,7 +1000,7 @@ class PackageManagerShellCommand extends ShellCommand { return sessionId; } private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName, private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName, boolean logSuccess) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); if ("-".equals(inPath)) { Loading Loading @@ -1004,6 +1056,27 @@ class PackageManagerShellCommand extends ShellCommand { } } private int doRemoveSplit(int sessionId, String splitName, boolean logSuccess) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); PackageInstaller.Session session = null; try { session = new PackageInstaller.Session( mInterface.getPackageInstaller().openSession(sessionId)); session.removeSplit(splitName); if (logSuccess) { pw.println("Success"); } return 0; } catch (IOException e) { pw.println("Error: failed to remove split; " + e.getMessage()); return 1; } finally { IoUtils.closeQuietly(session); } } private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); PackageInstaller.Session session = null; Loading