ApexManager: Request apexservice only when needed

Replacing the reference to apexservice with individual requests allows the service to be dynamically stopped when not in use and started on demand.

Bug: 149060287
Test: atest --test-mapping system/apex/apexd
Test: atest ApexManagerTest
Change-Id: I2d2173a692ec95a7a35b5a24ca466917e59c1da3
This commit is contained in:
Jon Spivack
2020-02-06 18:46:45 -08:00
parent 5b2f94a0aa
commit 91baff9782
2 changed files with 37 additions and 30 deletions

View File

@@ -32,9 +32,9 @@ import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageParser; import android.content.pm.PackageParser;
import android.content.pm.parsing.PackageInfoWithoutStateUtils; import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.os.Binder;
import android.os.Environment; import android.os.Environment;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Trace; import android.os.Trace;
import android.sysprop.ApexProperties; import android.sysprop.ApexProperties;
import android.util.ArrayMap; import android.util.ArrayMap;
@@ -61,7 +61,6 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -83,13 +82,7 @@ public abstract class ApexManager {
@Override @Override
protected ApexManager create() { protected ApexManager create() {
if (ApexProperties.updatable().orElse(false)) { if (ApexProperties.updatable().orElse(false)) {
try { return new ApexManagerImpl();
return new ApexManagerImpl(IApexService.Stub.asInterface(
ServiceManager.getServiceOrThrow("apexservice")));
} catch (ServiceManager.ServiceNotFoundException e) {
throw new IllegalStateException(
"Required service apexservice not available");
}
} else { } else {
return new ApexManagerFlattenedApex(); return new ApexManagerFlattenedApex();
} }
@@ -360,8 +353,7 @@ public abstract class ApexManager {
* APEX packages. * APEX packages.
*/ */
@VisibleForTesting @VisibleForTesting
static class ApexManagerImpl extends ApexManager { protected static class ApexManagerImpl extends ApexManager {
private final IApexService mApexService;
private final Object mLock = new Object(); private final Object mLock = new Object();
@GuardedBy("mLock") @GuardedBy("mLock")
@@ -390,10 +382,6 @@ public abstract class ApexManager {
@GuardedBy("mLock") @GuardedBy("mLock")
private ArrayMap<String, String> mPackageNameToApexModuleName; private ArrayMap<String, String> mPackageNameToApexModuleName;
ApexManagerImpl(IApexService apexService) {
mApexService = apexService;
}
/** /**
* Whether an APEX package is active or not. * Whether an APEX package is active or not.
* *
@@ -404,6 +392,19 @@ public abstract class ApexManager {
return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0; return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
} }
/**
* Retrieve the service from ServiceManager. If the service is not running, it will be
* started, and this function will block until it is ready.
*/
@VisibleForTesting
protected IApexService waitForApexService() {
try {
return IApexService.Stub.asInterface(Binder.waitForService("apexservice"));
} catch (RemoteException e) {
throw new IllegalStateException("Required service apexservice not available");
}
}
@Override @Override
public List<ActiveApexInfo> getActiveApexInfos() { public List<ActiveApexInfo> getActiveApexInfos() {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
@@ -413,7 +414,7 @@ public abstract class ApexManager {
t.traceBegin("getActiveApexInfos_noCache"); t.traceBegin("getActiveApexInfos_noCache");
try { try {
mActiveApexInfosCache = new ArraySet<>(); mActiveApexInfosCache = new ArraySet<>();
final ApexInfo[] activePackages = mApexService.getActivePackages(); final ApexInfo[] activePackages = waitForApexService().getActivePackages();
for (int i = 0; i < activePackages.length; i++) { for (int i = 0; i < activePackages.length; i++) {
ApexInfo apexInfo = activePackages[i]; ApexInfo apexInfo = activePackages[i];
mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo)); mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo));
@@ -451,7 +452,7 @@ public abstract class ApexManager {
try { try {
mAllPackagesCache = new ArrayList<>(); mAllPackagesCache = new ArrayList<>();
mPackageNameToApexModuleName = new ArrayMap<>(); mPackageNameToApexModuleName = new ArrayMap<>();
allPkgs = mApexService.getAllPackages(); allPkgs = waitForApexService().getAllPackages();
} catch (RemoteException re) { } catch (RemoteException re) {
Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
throw new RuntimeException(re); throw new RuntimeException(re);
@@ -610,7 +611,8 @@ public abstract class ApexManager {
@Override @Override
@Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
try { try {
ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId); ApexSessionInfo apexSessionInfo =
waitForApexService().getStagedSessionInfo(sessionId);
if (apexSessionInfo.isUnknown) { if (apexSessionInfo.isUnknown) {
return null; return null;
} }
@@ -625,7 +627,7 @@ public abstract class ApexManager {
ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException { ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException {
try { try {
final ApexInfoList apexInfoList = new ApexInfoList(); final ApexInfoList apexInfoList = new ApexInfoList();
mApexService.submitStagedSession(params, apexInfoList); waitForApexService().submitStagedSession(params, apexInfoList);
return apexInfoList; return apexInfoList;
} catch (RemoteException re) { } catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re); Slog.e(TAG, "Unable to contact apexservice", re);
@@ -640,7 +642,7 @@ public abstract class ApexManager {
@Override @Override
void markStagedSessionReady(int sessionId) throws PackageManagerException { void markStagedSessionReady(int sessionId) throws PackageManagerException {
try { try {
mApexService.markStagedSessionReady(sessionId); waitForApexService().markStagedSessionReady(sessionId);
} catch (RemoteException re) { } catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re); Slog.e(TAG, "Unable to contact apexservice", re);
throw new RuntimeException(re); throw new RuntimeException(re);
@@ -654,7 +656,7 @@ public abstract class ApexManager {
@Override @Override
void markStagedSessionSuccessful(int sessionId) { void markStagedSessionSuccessful(int sessionId) {
try { try {
mApexService.markStagedSessionSuccessful(sessionId); waitForApexService().markStagedSessionSuccessful(sessionId);
} catch (RemoteException re) { } catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re); Slog.e(TAG, "Unable to contact apexservice", re);
throw new RuntimeException(re); throw new RuntimeException(re);
@@ -673,7 +675,7 @@ public abstract class ApexManager {
@Override @Override
boolean revertActiveSessions() { boolean revertActiveSessions() {
try { try {
mApexService.revertActiveSessions(); waitForApexService().revertActiveSessions();
return true; return true;
} catch (RemoteException re) { } catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re); Slog.e(TAG, "Unable to contact apexservice", re);
@@ -687,7 +689,7 @@ public abstract class ApexManager {
@Override @Override
boolean abortStagedSession(int sessionId) throws PackageManagerException { boolean abortStagedSession(int sessionId) throws PackageManagerException {
try { try {
mApexService.abortStagedSession(sessionId); waitForApexService().abortStagedSession(sessionId);
return true; return true;
} catch (RemoteException re) { } catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re); Slog.e(TAG, "Unable to contact apexservice", re);
@@ -702,7 +704,7 @@ public abstract class ApexManager {
@Override @Override
boolean uninstallApex(String apexPackagePath) { boolean uninstallApex(String apexPackagePath) {
try { try {
mApexService.unstagePackages(Collections.singletonList(apexPackagePath)); waitForApexService().unstagePackages(Collections.singletonList(apexPackagePath));
return true; return true;
} catch (Exception e) { } catch (Exception e) {
return false; return false;
@@ -763,7 +765,7 @@ public abstract class ApexManager {
return -1; return -1;
} }
try { try {
return mApexService.snapshotCeData(userId, rollbackId, apexModuleName); return waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName);
} catch (Exception e) { } catch (Exception e) {
Slog.e(TAG, e.getMessage(), e); Slog.e(TAG, e.getMessage(), e);
return -1; return -1;
@@ -783,7 +785,7 @@ public abstract class ApexManager {
return false; return false;
} }
try { try {
mApexService.restoreCeData(userId, rollbackId, apexModuleName); waitForApexService().restoreCeData(userId, rollbackId, apexModuleName);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
Slog.e(TAG, e.getMessage(), e); Slog.e(TAG, e.getMessage(), e);
@@ -794,7 +796,7 @@ public abstract class ApexManager {
@Override @Override
public boolean destroyDeSnapshots(int rollbackId) { public boolean destroyDeSnapshots(int rollbackId) {
try { try {
mApexService.destroyDeSnapshots(rollbackId); waitForApexService().destroyDeSnapshots(rollbackId);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
Slog.e(TAG, e.getMessage(), e); Slog.e(TAG, e.getMessage(), e);
@@ -805,7 +807,7 @@ public abstract class ApexManager {
@Override @Override
public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
try { try {
mApexService.destroyCeSnapshotsNotSpecified(userId, retainRollbackIds); waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
Slog.e(TAG, e.getMessage(), e); Slog.e(TAG, e.getMessage(), e);
@@ -849,7 +851,7 @@ public abstract class ApexManager {
ipw.println(); ipw.println();
ipw.println("APEX session state:"); ipw.println("APEX session state:");
ipw.increaseIndent(); ipw.increaseIndent();
final ApexSessionInfo[] sessions = mApexService.getSessions(); final ApexSessionInfo[] sessions = waitForApexService().getSessions();
for (ApexSessionInfo si : sessions) { for (ApexSessionInfo si : sessions) {
ipw.println("Session ID: " + si.sessionId); ipw.println("Session ID: " + si.sessionId);
ipw.increaseIndent(); ipw.increaseIndent();

View File

@@ -21,8 +21,10 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -58,6 +60,7 @@ import java.io.InputStream;
@SmallTest @SmallTest
@Presubmit @Presubmit
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class ApexManagerTest { public class ApexManagerTest {
private static final String TEST_APEX_PKG = "com.android.apex.test"; private static final String TEST_APEX_PKG = "com.android.apex.test";
private static final int TEST_SESSION_ID = 99999999; private static final int TEST_SESSION_ID = 99999999;
@@ -71,7 +74,9 @@ public class ApexManagerTest {
@Before @Before
public void setUp() throws RemoteException { public void setUp() throws RemoteException {
mContext = InstrumentationRegistry.getInstrumentation().getContext(); mContext = InstrumentationRegistry.getInstrumentation().getContext();
mApexManager = new ApexManager.ApexManagerImpl(mApexService); ApexManager.ApexManagerImpl managerImpl = spy(new ApexManager.ApexManagerImpl());
doReturn(mApexService).when(managerImpl).waitForApexService();
mApexManager = managerImpl;
mPackageParser2 = new PackageParser2(null, false, null, null, null); mPackageParser2 = new PackageParser2(null, false, null, null, null);
} }