Loading services/core/java/com/android/server/pm/ApexManager.java 0 → 100644 +221 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.s */ package com.android.server.pm; import android.annotation.NonNull; import android.annotation.Nullable; import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.IApexService; import android.content.pm.PackageInfo; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; /** * ApexManager class handles communications with the apex service to perform operation and queries, * as well as providing caching to avoid unnecessary calls to the service. */ class ApexManager { static final String TAG = "ApexManager"; private final IApexService mApexService; private final Map<String, PackageInfo> mActivePackagesCache; ApexManager() { mApexService = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); mActivePackagesCache = populateActivePackagesCache(); } @NonNull private Map<String, PackageInfo> populateActivePackagesCache() { try { List<PackageInfo> list = new ArrayList<>(); final ApexInfo[] activePkgs = mApexService.getActivePackages(); for (ApexInfo ai : activePkgs) { // If the device is using flattened APEX, don't report any APEX // packages since they won't be managed or updated by PackageManager. if ((new File(ai.packagePath)).isDirectory()) { break; } try { list.add(PackageParser.generatePackageInfoFromApex( new File(ai.packagePath), true /* collect certs */)); } catch (PackageParserException pe) { throw new IllegalStateException("Unable to parse: " + ai, pe); } } return list.stream().collect(Collectors.toMap(p -> p.packageName, Function.identity())); } catch (RemoteException re) { Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); throw new RuntimeException(re); } } /** * Retrieves information about an active APEX package. * * @param packageName the package name to look for. Note that this is the package name reported * in the APK container manifest (i.e. AndroidManifest.xml), which might * differ from the one reported in the APEX manifest (i.e. * apex_manifest.json). * @return a PackageInfo object with the information about the package, or null if the package * is not found. */ @Nullable PackageInfo getActivePackage(String packageName) { return mActivePackagesCache.get(packageName); } /** * Retrieves information about all active APEX packages. * * @return a Collection of PackageInfo object, each one containing information about a different * active package. */ Collection<PackageInfo> getActivePackages() { return mActivePackagesCache.values(); } /** * Retrieves information about an apexd staged session i.e. the internal state used by apexd to * track the different states of a session. * * @param sessionId the identifier of the session. * @return an ApexSessionInfo object, or null if the session is not known. */ @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { try { ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId); if (apexSessionInfo.isUnknown) { return null; } return apexSessionInfo; } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); throw new RuntimeException(re); } } /** * Submit a staged session to apex service. This causes the apex service to perform some initial * verification and accept or reject the session. Submitting a session successfully is not * enough for it to be activated at the next boot, the caller needs to call * {@link #markStagedSessionReady(int)}. * * @param sessionId the identifier of the {@link PackageInstallerSession} being submitted. * @param childSessionIds if {@code sessionId} is a multi-package session, this should contain * an array of identifiers of all the child sessions. Otherwise it should * be an empty array. * @param apexInfoList this is an output parameter, which needs to be initialized by tha caller * and will be filled with a list of {@link ApexInfo} objects, each of which * contains metadata about one of the packages being submitted as part of * the session. * @return whether the submission of the session was successful. */ boolean submitStagedSession( int sessionId, @NonNull int[] childSessionIds, @NonNull ApexInfoList apexInfoList) { try { return mApexService.submitStagedSession(sessionId, childSessionIds, apexInfoList); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); throw new RuntimeException(re); } } /** * Mark a staged session previously submitted using {@cde submitStagedSession} as ready to be * applied at next reboot. * * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready. * @return true upon success, false if the session is unknown. */ boolean markStagedSessionReady(int sessionId) { try { return mApexService.markStagedSessionReady(sessionId); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); throw new RuntimeException(re); } } /** * Dumps various state information to the provided {@link PrintWriter} object. * * @param pw the {@link PrintWriter} object to send information to. * @param packageName a {@link String} containing a package name, or {@code null}. If set, only * information about that specific package will be dumped. */ void dump(PrintWriter pw, @Nullable String packageName) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); ipw.println(); ipw.println("Active APEX packages:"); ipw.increaseIndent(); try { populateActivePackagesCache(); for (PackageInfo pi : mActivePackagesCache.values()) { if (packageName != null && !packageName.equals(pi.packageName)) { continue; } ipw.println(pi.packageName); ipw.increaseIndent(); ipw.println("Version: " + pi.versionCode); ipw.println("Path: " + pi.applicationInfo.sourceDir); ipw.decreaseIndent(); } ipw.decreaseIndent(); ipw.println(); ipw.println("APEX session state:"); ipw.increaseIndent(); final ApexSessionInfo[] sessions = mApexService.getSessions(); for (ApexSessionInfo si : sessions) { ipw.println("Session ID: " + Integer.toString(si.sessionId)); ipw.increaseIndent(); if (si.isUnknown) { ipw.println("State: UNKNOWN"); } else if (si.isVerified) { ipw.println("State: VERIFIED"); } else if (si.isStaged) { ipw.println("State: STAGED"); } else if (si.isActivated) { ipw.println("State: ACTIVATED"); } else if (si.isActivationPendingRetry) { ipw.println("State: ACTIVATION PENDING RETRY"); } else if (si.isActivationFailed) { ipw.println("State: ACTIVATION FAILED"); } ipw.decreaseIndent(); } ipw.decreaseIndent(); } catch (RemoteException e) { ipw.println("Couldn't communicate with apexd."); } } } services/core/java/com/android/server/pm/PackageInstallerService.java +2 −2 Original line number Diff line number Diff line Loading @@ -186,7 +186,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } }; public PackageInstallerService(Context context, PackageManagerService pm) { public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) { mContext = context; mPm = pm; mPermissionManager = LocalServices.getService(PermissionManagerInternal.class); Loading @@ -204,7 +204,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions"); mSessionsDir.mkdirs(); mStagingManager = new StagingManager(pm, this); mStagingManager = new StagingManager(pm, this, am); } private void setBootCompleted() { Loading services/core/java/com/android/server/pm/PackageManagerService.java +7 −91 Original line number Diff line number Diff line Loading @@ -119,9 +119,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.apex.ApexInfo; import android.apex.ApexSessionInfo; import android.apex.IApexService; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppDetailsActivity; Loading Loading @@ -734,10 +731,10 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>(); private PackageManager mPackageManager; private final ModuleInfoProvider mModuleInfoProvider; private final ApexManager mApexManager; class PackageParserCallback implements PackageParser.Callback { @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); Loading Loading @@ -3075,7 +3072,8 @@ public class PackageManagerService extends IPackageManager.Stub } } mInstallerService = new PackageInstallerService(context, this); mApexManager = new ApexManager(); mInstallerService = new PackageInstallerService(context, this, mApexManager); final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { Loading Loading @@ -3935,27 +3933,7 @@ public class PackageManagerService extends IPackageManager.Stub } // if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) { //TODO(b/123052859) Don't do file operations every time there is a query. final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); if (apex != null) { try { final ApexInfo activePkg = apex.getActivePackage(packageName); if (activePkg != null && !TextUtils.isEmpty(activePkg.packagePath)) { try { return PackageParser.generatePackageInfoFromApex( new File(activePkg.packagePath), true /* collect certs */); } catch (PackageParserException pe) { Log.e(TAG, "Unable to parse package at " + activePkg.packagePath, pe); } } } catch (RemoteException e) { Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString()); } } else { Log.e(TAG, "Unable to connect to apexservice for querying packages."); } return mApexManager.getActivePackage(packageName); } } return null; Loading Loading @@ -7852,25 +7830,7 @@ public class PackageManagerService extends IPackageManager.Stub if (listApex) { // TODO(b/119767311): include uninstalled/inactive APEX if // MATCH_UNINSTALLED_PACKAGES is set. final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); if (apex != null) { try { final ApexInfo[] activePkgs = apex.getActivePackages(); for (ApexInfo ai : activePkgs) { try { list.add(PackageParser.generatePackageInfoFromApex( new File(ai.packagePath), true /* collect certs */)); } catch (PackageParserException pe) { throw new IllegalStateException("Unable to parse: " + ai, pe); } } } catch (RemoteException e) { Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString()); } } else { Log.e(TAG, "Unable to connect to apexservice for querying packages."); } list.addAll(mApexManager.getActivePackages()); } return new ParceledListSlice<>(list); } Loading Loading @@ -21325,51 +21285,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); ipw.println(); ipw.println("Active APEX packages:"); ipw.increaseIndent(); final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); try { final ApexInfo[] activeApexes = apex.getActivePackages(); for (ApexInfo ai : activeApexes) { if (packageName != null && !packageName.equals(ai.packageName)) { continue; } ipw.println(ai.packageName); ipw.increaseIndent(); ipw.println("Version: " + Long.toString(ai.versionCode)); ipw.println("Path: " + ai.packagePath); ipw.decreaseIndent(); } ipw.decreaseIndent(); ipw.println(); ipw.println("APEX session state:"); ipw.increaseIndent(); final ApexSessionInfo[] sessions = apex.getSessions(); for (ApexSessionInfo si : sessions) { ipw.println("Session ID: " + Integer.toString(si.sessionId)); ipw.increaseIndent(); if (si.isUnknown) { ipw.println("State: UNKNOWN"); } else if (si.isVerified) { ipw.println("State: VERIFIED"); } else if (si.isStaged) { ipw.println("State: STAGED"); } else if (si.isActivated) { ipw.println("State: ACTIVATED"); } else if (si.isActivationPendingRetry) { ipw.println("State: ACTIVATION PENDING RETRY"); } else if (si.isActivationFailed) { ipw.println("State: ACTIVATION FAILED"); } ipw.decreaseIndent(); } ipw.decreaseIndent(); } catch (RemoteException e) { ipw.println("Couldn't communicate with apexd."); } mApexManager.dump(pw, packageName); } } services/core/java/com/android/server/pm/StagingManager.java +23 −60 Original line number Diff line number Diff line Loading @@ -20,12 +20,12 @@ import android.annotation.NonNull; import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.IApexService; import android.content.Context; import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; Loading @@ -41,7 +41,6 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.util.apk.ApkSignatureVerifier; Loading @@ -68,14 +67,16 @@ public class StagingManager { private final PackageInstallerService mPi; private final PackageManagerService mPm; private final ApexManager mApexManager; private final Handler mBgHandler; @GuardedBy("mStagedSessions") private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>(); StagingManager(PackageManagerService pm, PackageInstallerService pi) { StagingManager(PackageManagerService pm, PackageInstallerService pi, ApexManager am) { mPm = pm; mPi = pi; mApexManager = am; mBgHandler = BackgroundThread.getHandler(); } Loading @@ -100,7 +101,7 @@ public class StagingManager { return new ParceledListSlice<>(result); } private static boolean validateApexSignature(String apexPath, String packageName) { private boolean validateApexSignature(String apexPath, String packageName) { final SigningDetails signingDetails; try { signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR); Loading @@ -109,17 +110,9 @@ public class StagingManager { return false; } final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); final ApexInfo apexInfo; try { apexInfo = apex.getActivePackage(packageName); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact APEXD", re); return false; } final PackageInfo packageInfo = mApexManager.getActivePackage(packageName); if (apexInfo == null || TextUtils.isEmpty(apexInfo.packageName)) { if (packageInfo == null) { // TODO: What is the right thing to do here ? This implies there's no active package // with the given name. This should never be the case in production (where we only // accept updates to existing APEXes) but may be required for testing. Loading @@ -129,9 +122,10 @@ public class StagingManager { final SigningDetails existingSigningDetails; try { existingSigningDetails = ApkSignatureVerifier.verify( apexInfo.packagePath, SignatureSchemeVersion.JAR); packageInfo.applicationInfo.sourceDir, SignatureSchemeVersion.JAR); } catch (PackageParserException e) { Slog.e(TAG, "Unable to parse APEX package: " + apexInfo.packagePath, e); Slog.e(TAG, "Unable to parse APEX package: " + packageInfo.applicationInfo.sourceDir, e); return false; } Loading @@ -143,10 +137,10 @@ public class StagingManager { return false; } private static boolean submitSessionToApexService(@NonNull PackageInstallerSession session, private boolean submitSessionToApexService(@NonNull PackageInstallerSession session, List<PackageInstallerSession> childSessions, ApexInfoList apexInfoList) { return sendSubmitStagedSessionRequest( return mApexManager.submitStagedSession( session.sessionId, childSessions != null ? childSessions.stream().mapToInt(s -> s.sessionId).toArray() : Loading @@ -154,33 +148,6 @@ public class StagingManager { apexInfoList); } private static boolean sendSubmitStagedSessionRequest( int sessionId, int[] childSessionIds, ApexInfoList apexInfoList) { final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); boolean success; try { success = apex.submitStagedSession(sessionId, childSessionIds, apexInfoList); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); return false; } return success; } private static boolean sendMarkStagedSessionReadyRequest(int sessionId) { final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); boolean success; try { success = apex.markStagedSessionReady(sessionId); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); return false; } return success; } private static boolean isApexSession(@NonNull PackageInstallerSession session) { return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0; } Loading Loading @@ -260,7 +227,7 @@ public class StagingManager { } session.setStagedSessionReady(); if (!sendMarkStagedSessionReadyRequest(session.sessionId)) { if (!mApexManager.markStagedSessionReady(session.sessionId)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "APEX staging failed, check logcat messages from apexd for more " + "details."); Loading @@ -284,16 +251,12 @@ public class StagingManager { private void resumeSession(@NonNull PackageInstallerSession session) { if (sessionContainsApex(session)) { // Check with apexservice whether the apex // packages have been activated. final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); ApexSessionInfo apexSessionInfo; try { apexSessionInfo = apex.getStagedSessionInfo(session.sessionId); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); // TODO should we retry here? Mark the session as failed? // Check with apexservice whether the apex packages have been activated. ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId); if (apexSessionInfo == null) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "apexd did not know anything about a staged session supposed to be" + "activated"); return; } if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) { Loading Loading @@ -323,7 +286,7 @@ public class StagingManager { // The APEX part of the session is activated, proceed with the installation of APKs. if (!installApksInSession(session)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "APEX activation failed. Check logcat messages from apexd for " "Staged installation of APKs failed. Check logcat messages for" + "more information."); return; } Loading Loading
services/core/java/com/android/server/pm/ApexManager.java 0 → 100644 +221 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.s */ package com.android.server.pm; import android.annotation.NonNull; import android.annotation.Nullable; import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.IApexService; import android.content.pm.PackageInfo; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; /** * ApexManager class handles communications with the apex service to perform operation and queries, * as well as providing caching to avoid unnecessary calls to the service. */ class ApexManager { static final String TAG = "ApexManager"; private final IApexService mApexService; private final Map<String, PackageInfo> mActivePackagesCache; ApexManager() { mApexService = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); mActivePackagesCache = populateActivePackagesCache(); } @NonNull private Map<String, PackageInfo> populateActivePackagesCache() { try { List<PackageInfo> list = new ArrayList<>(); final ApexInfo[] activePkgs = mApexService.getActivePackages(); for (ApexInfo ai : activePkgs) { // If the device is using flattened APEX, don't report any APEX // packages since they won't be managed or updated by PackageManager. if ((new File(ai.packagePath)).isDirectory()) { break; } try { list.add(PackageParser.generatePackageInfoFromApex( new File(ai.packagePath), true /* collect certs */)); } catch (PackageParserException pe) { throw new IllegalStateException("Unable to parse: " + ai, pe); } } return list.stream().collect(Collectors.toMap(p -> p.packageName, Function.identity())); } catch (RemoteException re) { Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); throw new RuntimeException(re); } } /** * Retrieves information about an active APEX package. * * @param packageName the package name to look for. Note that this is the package name reported * in the APK container manifest (i.e. AndroidManifest.xml), which might * differ from the one reported in the APEX manifest (i.e. * apex_manifest.json). * @return a PackageInfo object with the information about the package, or null if the package * is not found. */ @Nullable PackageInfo getActivePackage(String packageName) { return mActivePackagesCache.get(packageName); } /** * Retrieves information about all active APEX packages. * * @return a Collection of PackageInfo object, each one containing information about a different * active package. */ Collection<PackageInfo> getActivePackages() { return mActivePackagesCache.values(); } /** * Retrieves information about an apexd staged session i.e. the internal state used by apexd to * track the different states of a session. * * @param sessionId the identifier of the session. * @return an ApexSessionInfo object, or null if the session is not known. */ @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { try { ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId); if (apexSessionInfo.isUnknown) { return null; } return apexSessionInfo; } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); throw new RuntimeException(re); } } /** * Submit a staged session to apex service. This causes the apex service to perform some initial * verification and accept or reject the session. Submitting a session successfully is not * enough for it to be activated at the next boot, the caller needs to call * {@link #markStagedSessionReady(int)}. * * @param sessionId the identifier of the {@link PackageInstallerSession} being submitted. * @param childSessionIds if {@code sessionId} is a multi-package session, this should contain * an array of identifiers of all the child sessions. Otherwise it should * be an empty array. * @param apexInfoList this is an output parameter, which needs to be initialized by tha caller * and will be filled with a list of {@link ApexInfo} objects, each of which * contains metadata about one of the packages being submitted as part of * the session. * @return whether the submission of the session was successful. */ boolean submitStagedSession( int sessionId, @NonNull int[] childSessionIds, @NonNull ApexInfoList apexInfoList) { try { return mApexService.submitStagedSession(sessionId, childSessionIds, apexInfoList); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); throw new RuntimeException(re); } } /** * Mark a staged session previously submitted using {@cde submitStagedSession} as ready to be * applied at next reboot. * * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready. * @return true upon success, false if the session is unknown. */ boolean markStagedSessionReady(int sessionId) { try { return mApexService.markStagedSessionReady(sessionId); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); throw new RuntimeException(re); } } /** * Dumps various state information to the provided {@link PrintWriter} object. * * @param pw the {@link PrintWriter} object to send information to. * @param packageName a {@link String} containing a package name, or {@code null}. If set, only * information about that specific package will be dumped. */ void dump(PrintWriter pw, @Nullable String packageName) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); ipw.println(); ipw.println("Active APEX packages:"); ipw.increaseIndent(); try { populateActivePackagesCache(); for (PackageInfo pi : mActivePackagesCache.values()) { if (packageName != null && !packageName.equals(pi.packageName)) { continue; } ipw.println(pi.packageName); ipw.increaseIndent(); ipw.println("Version: " + pi.versionCode); ipw.println("Path: " + pi.applicationInfo.sourceDir); ipw.decreaseIndent(); } ipw.decreaseIndent(); ipw.println(); ipw.println("APEX session state:"); ipw.increaseIndent(); final ApexSessionInfo[] sessions = mApexService.getSessions(); for (ApexSessionInfo si : sessions) { ipw.println("Session ID: " + Integer.toString(si.sessionId)); ipw.increaseIndent(); if (si.isUnknown) { ipw.println("State: UNKNOWN"); } else if (si.isVerified) { ipw.println("State: VERIFIED"); } else if (si.isStaged) { ipw.println("State: STAGED"); } else if (si.isActivated) { ipw.println("State: ACTIVATED"); } else if (si.isActivationPendingRetry) { ipw.println("State: ACTIVATION PENDING RETRY"); } else if (si.isActivationFailed) { ipw.println("State: ACTIVATION FAILED"); } ipw.decreaseIndent(); } ipw.decreaseIndent(); } catch (RemoteException e) { ipw.println("Couldn't communicate with apexd."); } } }
services/core/java/com/android/server/pm/PackageInstallerService.java +2 −2 Original line number Diff line number Diff line Loading @@ -186,7 +186,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } }; public PackageInstallerService(Context context, PackageManagerService pm) { public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) { mContext = context; mPm = pm; mPermissionManager = LocalServices.getService(PermissionManagerInternal.class); Loading @@ -204,7 +204,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions"); mSessionsDir.mkdirs(); mStagingManager = new StagingManager(pm, this); mStagingManager = new StagingManager(pm, this, am); } private void setBootCompleted() { Loading
services/core/java/com/android/server/pm/PackageManagerService.java +7 −91 Original line number Diff line number Diff line Loading @@ -119,9 +119,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.apex.ApexInfo; import android.apex.ApexSessionInfo; import android.apex.IApexService; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppDetailsActivity; Loading Loading @@ -734,10 +731,10 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>(); private PackageManager mPackageManager; private final ModuleInfoProvider mModuleInfoProvider; private final ApexManager mApexManager; class PackageParserCallback implements PackageParser.Callback { @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); Loading Loading @@ -3075,7 +3072,8 @@ public class PackageManagerService extends IPackageManager.Stub } } mInstallerService = new PackageInstallerService(context, this); mApexManager = new ApexManager(); mInstallerService = new PackageInstallerService(context, this, mApexManager); final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { Loading Loading @@ -3935,27 +3933,7 @@ public class PackageManagerService extends IPackageManager.Stub } // if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) { //TODO(b/123052859) Don't do file operations every time there is a query. final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); if (apex != null) { try { final ApexInfo activePkg = apex.getActivePackage(packageName); if (activePkg != null && !TextUtils.isEmpty(activePkg.packagePath)) { try { return PackageParser.generatePackageInfoFromApex( new File(activePkg.packagePath), true /* collect certs */); } catch (PackageParserException pe) { Log.e(TAG, "Unable to parse package at " + activePkg.packagePath, pe); } } } catch (RemoteException e) { Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString()); } } else { Log.e(TAG, "Unable to connect to apexservice for querying packages."); } return mApexManager.getActivePackage(packageName); } } return null; Loading Loading @@ -7852,25 +7830,7 @@ public class PackageManagerService extends IPackageManager.Stub if (listApex) { // TODO(b/119767311): include uninstalled/inactive APEX if // MATCH_UNINSTALLED_PACKAGES is set. final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); if (apex != null) { try { final ApexInfo[] activePkgs = apex.getActivePackages(); for (ApexInfo ai : activePkgs) { try { list.add(PackageParser.generatePackageInfoFromApex( new File(ai.packagePath), true /* collect certs */)); } catch (PackageParserException pe) { throw new IllegalStateException("Unable to parse: " + ai, pe); } } } catch (RemoteException e) { Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString()); } } else { Log.e(TAG, "Unable to connect to apexservice for querying packages."); } list.addAll(mApexManager.getActivePackages()); } return new ParceledListSlice<>(list); } Loading Loading @@ -21325,51 +21285,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); ipw.println(); ipw.println("Active APEX packages:"); ipw.increaseIndent(); final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); try { final ApexInfo[] activeApexes = apex.getActivePackages(); for (ApexInfo ai : activeApexes) { if (packageName != null && !packageName.equals(ai.packageName)) { continue; } ipw.println(ai.packageName); ipw.increaseIndent(); ipw.println("Version: " + Long.toString(ai.versionCode)); ipw.println("Path: " + ai.packagePath); ipw.decreaseIndent(); } ipw.decreaseIndent(); ipw.println(); ipw.println("APEX session state:"); ipw.increaseIndent(); final ApexSessionInfo[] sessions = apex.getSessions(); for (ApexSessionInfo si : sessions) { ipw.println("Session ID: " + Integer.toString(si.sessionId)); ipw.increaseIndent(); if (si.isUnknown) { ipw.println("State: UNKNOWN"); } else if (si.isVerified) { ipw.println("State: VERIFIED"); } else if (si.isStaged) { ipw.println("State: STAGED"); } else if (si.isActivated) { ipw.println("State: ACTIVATED"); } else if (si.isActivationPendingRetry) { ipw.println("State: ACTIVATION PENDING RETRY"); } else if (si.isActivationFailed) { ipw.println("State: ACTIVATION FAILED"); } ipw.decreaseIndent(); } ipw.decreaseIndent(); } catch (RemoteException e) { ipw.println("Couldn't communicate with apexd."); } mApexManager.dump(pw, packageName); } }
services/core/java/com/android/server/pm/StagingManager.java +23 −60 Original line number Diff line number Diff line Loading @@ -20,12 +20,12 @@ import android.annotation.NonNull; import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.IApexService; import android.content.Context; import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; Loading @@ -41,7 +41,6 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.util.apk.ApkSignatureVerifier; Loading @@ -68,14 +67,16 @@ public class StagingManager { private final PackageInstallerService mPi; private final PackageManagerService mPm; private final ApexManager mApexManager; private final Handler mBgHandler; @GuardedBy("mStagedSessions") private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>(); StagingManager(PackageManagerService pm, PackageInstallerService pi) { StagingManager(PackageManagerService pm, PackageInstallerService pi, ApexManager am) { mPm = pm; mPi = pi; mApexManager = am; mBgHandler = BackgroundThread.getHandler(); } Loading @@ -100,7 +101,7 @@ public class StagingManager { return new ParceledListSlice<>(result); } private static boolean validateApexSignature(String apexPath, String packageName) { private boolean validateApexSignature(String apexPath, String packageName) { final SigningDetails signingDetails; try { signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR); Loading @@ -109,17 +110,9 @@ public class StagingManager { return false; } final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); final ApexInfo apexInfo; try { apexInfo = apex.getActivePackage(packageName); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact APEXD", re); return false; } final PackageInfo packageInfo = mApexManager.getActivePackage(packageName); if (apexInfo == null || TextUtils.isEmpty(apexInfo.packageName)) { if (packageInfo == null) { // TODO: What is the right thing to do here ? This implies there's no active package // with the given name. This should never be the case in production (where we only // accept updates to existing APEXes) but may be required for testing. Loading @@ -129,9 +122,10 @@ public class StagingManager { final SigningDetails existingSigningDetails; try { existingSigningDetails = ApkSignatureVerifier.verify( apexInfo.packagePath, SignatureSchemeVersion.JAR); packageInfo.applicationInfo.sourceDir, SignatureSchemeVersion.JAR); } catch (PackageParserException e) { Slog.e(TAG, "Unable to parse APEX package: " + apexInfo.packagePath, e); Slog.e(TAG, "Unable to parse APEX package: " + packageInfo.applicationInfo.sourceDir, e); return false; } Loading @@ -143,10 +137,10 @@ public class StagingManager { return false; } private static boolean submitSessionToApexService(@NonNull PackageInstallerSession session, private boolean submitSessionToApexService(@NonNull PackageInstallerSession session, List<PackageInstallerSession> childSessions, ApexInfoList apexInfoList) { return sendSubmitStagedSessionRequest( return mApexManager.submitStagedSession( session.sessionId, childSessions != null ? childSessions.stream().mapToInt(s -> s.sessionId).toArray() : Loading @@ -154,33 +148,6 @@ public class StagingManager { apexInfoList); } private static boolean sendSubmitStagedSessionRequest( int sessionId, int[] childSessionIds, ApexInfoList apexInfoList) { final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); boolean success; try { success = apex.submitStagedSession(sessionId, childSessionIds, apexInfoList); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); return false; } return success; } private static boolean sendMarkStagedSessionReadyRequest(int sessionId) { final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); boolean success; try { success = apex.markStagedSessionReady(sessionId); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); return false; } return success; } private static boolean isApexSession(@NonNull PackageInstallerSession session) { return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0; } Loading Loading @@ -260,7 +227,7 @@ public class StagingManager { } session.setStagedSessionReady(); if (!sendMarkStagedSessionReadyRequest(session.sessionId)) { if (!mApexManager.markStagedSessionReady(session.sessionId)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "APEX staging failed, check logcat messages from apexd for more " + "details."); Loading @@ -284,16 +251,12 @@ public class StagingManager { private void resumeSession(@NonNull PackageInstallerSession session) { if (sessionContainsApex(session)) { // Check with apexservice whether the apex // packages have been activated. final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); ApexSessionInfo apexSessionInfo; try { apexSessionInfo = apex.getStagedSessionInfo(session.sessionId); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); // TODO should we retry here? Mark the session as failed? // Check with apexservice whether the apex packages have been activated. ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId); if (apexSessionInfo == null) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "apexd did not know anything about a staged session supposed to be" + "activated"); return; } if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) { Loading Loading @@ -323,7 +286,7 @@ public class StagingManager { // The APEX part of the session is activated, proceed with the installation of APKs. if (!installApksInSession(session)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "APEX activation failed. Check logcat messages from apexd for " "Staged installation of APKs failed. Check logcat messages for" + "more information."); return; } Loading