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.PackageParser;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.os.Binder;
import android.os.Environment;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Trace;
import android.sysprop.ApexProperties;
import android.util.ArrayMap;
@@ -61,7 +61,6 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -83,13 +82,7 @@ public abstract class ApexManager {
@Override
protected ApexManager create() {
if (ApexProperties.updatable().orElse(false)) {
try {
return new ApexManagerImpl(IApexService.Stub.asInterface(
ServiceManager.getServiceOrThrow("apexservice")));
} catch (ServiceManager.ServiceNotFoundException e) {
throw new IllegalStateException(
"Required service apexservice not available");
}
return new ApexManagerImpl();
} else {
return new ApexManagerFlattenedApex();
}
@@ -360,8 +353,7 @@ public abstract class ApexManager {
* APEX packages.
*/
@VisibleForTesting
static class ApexManagerImpl extends ApexManager {
private final IApexService mApexService;
protected static class ApexManagerImpl extends ApexManager {
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -390,10 +382,6 @@ public abstract class ApexManager {
@GuardedBy("mLock")
private ArrayMap<String, String> mPackageNameToApexModuleName;
ApexManagerImpl(IApexService apexService) {
mApexService = apexService;
}
/**
* Whether an APEX package is active or not.
*
@@ -404,6 +392,19 @@ public abstract class ApexManager {
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
public List<ActiveApexInfo> getActiveApexInfos() {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
@@ -413,7 +414,7 @@ public abstract class ApexManager {
t.traceBegin("getActiveApexInfos_noCache");
try {
mActiveApexInfosCache = new ArraySet<>();
final ApexInfo[] activePackages = mApexService.getActivePackages();
final ApexInfo[] activePackages = waitForApexService().getActivePackages();
for (int i = 0; i < activePackages.length; i++) {
ApexInfo apexInfo = activePackages[i];
mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo));
@@ -451,7 +452,7 @@ public abstract class ApexManager {
try {
mAllPackagesCache = new ArrayList<>();
mPackageNameToApexModuleName = new ArrayMap<>();
allPkgs = mApexService.getAllPackages();
allPkgs = waitForApexService().getAllPackages();
} catch (RemoteException re) {
Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
throw new RuntimeException(re);
@@ -610,7 +611,8 @@ public abstract class ApexManager {
@Override
@Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
try {
ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
ApexSessionInfo apexSessionInfo =
waitForApexService().getStagedSessionInfo(sessionId);
if (apexSessionInfo.isUnknown) {
return null;
}
@@ -625,7 +627,7 @@ public abstract class ApexManager {
ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException {
try {
final ApexInfoList apexInfoList = new ApexInfoList();
mApexService.submitStagedSession(params, apexInfoList);
waitForApexService().submitStagedSession(params, apexInfoList);
return apexInfoList;
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
@@ -640,7 +642,7 @@ public abstract class ApexManager {
@Override
void markStagedSessionReady(int sessionId) throws PackageManagerException {
try {
mApexService.markStagedSessionReady(sessionId);
waitForApexService().markStagedSessionReady(sessionId);
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
throw new RuntimeException(re);
@@ -654,7 +656,7 @@ public abstract class ApexManager {
@Override
void markStagedSessionSuccessful(int sessionId) {
try {
mApexService.markStagedSessionSuccessful(sessionId);
waitForApexService().markStagedSessionSuccessful(sessionId);
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
throw new RuntimeException(re);
@@ -673,7 +675,7 @@ public abstract class ApexManager {
@Override
boolean revertActiveSessions() {
try {
mApexService.revertActiveSessions();
waitForApexService().revertActiveSessions();
return true;
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
@@ -687,7 +689,7 @@ public abstract class ApexManager {
@Override
boolean abortStagedSession(int sessionId) throws PackageManagerException {
try {
mApexService.abortStagedSession(sessionId);
waitForApexService().abortStagedSession(sessionId);
return true;
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
@@ -702,7 +704,7 @@ public abstract class ApexManager {
@Override
boolean uninstallApex(String apexPackagePath) {
try {
mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
waitForApexService().unstagePackages(Collections.singletonList(apexPackagePath));
return true;
} catch (Exception e) {
return false;
@@ -763,7 +765,7 @@ public abstract class ApexManager {
return -1;
}
try {
return mApexService.snapshotCeData(userId, rollbackId, apexModuleName);
return waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName);
} catch (Exception e) {
Slog.e(TAG, e.getMessage(), e);
return -1;
@@ -783,7 +785,7 @@ public abstract class ApexManager {
return false;
}
try {
mApexService.restoreCeData(userId, rollbackId, apexModuleName);
waitForApexService().restoreCeData(userId, rollbackId, apexModuleName);
return true;
} catch (Exception e) {
Slog.e(TAG, e.getMessage(), e);
@@ -794,7 +796,7 @@ public abstract class ApexManager {
@Override
public boolean destroyDeSnapshots(int rollbackId) {
try {
mApexService.destroyDeSnapshots(rollbackId);
waitForApexService().destroyDeSnapshots(rollbackId);
return true;
} catch (Exception e) {
Slog.e(TAG, e.getMessage(), e);
@@ -805,7 +807,7 @@ public abstract class ApexManager {
@Override
public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
try {
mApexService.destroyCeSnapshotsNotSpecified(userId, retainRollbackIds);
waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds);
return true;
} catch (Exception e) {
Slog.e(TAG, e.getMessage(), e);
@@ -849,7 +851,7 @@ public abstract class ApexManager {
ipw.println();
ipw.println("APEX session state:");
ipw.increaseIndent();
final ApexSessionInfo[] sessions = mApexService.getSessions();
final ApexSessionInfo[] sessions = waitForApexService().getSessions();
for (ApexSessionInfo si : sessions) {
ipw.println("Session ID: " + si.sessionId);
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.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -58,6 +60,7 @@ import java.io.InputStream;
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ApexManagerTest {
private static final String TEST_APEX_PKG = "com.android.apex.test";
private static final int TEST_SESSION_ID = 99999999;
@@ -71,7 +74,9 @@ public class ApexManagerTest {
@Before
public void setUp() throws RemoteException {
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);
}