Notify StagedApexObservers when there is a change in set of staged APEX am: fe58e6f7d1 am: 9178f580b5
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15638981 Change-Id: I3425feb62ce0bf31484f84e86515362b0aec87cf
This commit is contained in:
@@ -29,7 +29,9 @@ import android.content.IIntentSender;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.IntentSender;
|
import android.content.IntentSender;
|
||||||
|
import android.content.pm.ApexStagedEvent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.IStagedApexObserver;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageInstaller;
|
import android.content.pm.PackageInstaller;
|
||||||
import android.content.pm.PackageInstaller.SessionInfo;
|
import android.content.pm.PackageInstaller.SessionInfo;
|
||||||
@@ -102,7 +104,8 @@ public class StagingManager {
|
|||||||
private final ApexManager mApexManager;
|
private final ApexManager mApexManager;
|
||||||
private final PowerManager mPowerManager;
|
private final PowerManager mPowerManager;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final PreRebootVerificationHandler mPreRebootVerificationHandler;
|
@VisibleForTesting
|
||||||
|
final PreRebootVerificationHandler mPreRebootVerificationHandler;
|
||||||
private final Supplier<PackageParser2> mPackageParserSupplier;
|
private final Supplier<PackageParser2> mPackageParserSupplier;
|
||||||
|
|
||||||
private final File mFailureReasonFile = new File("/metadata/staged-install/failure_reason.txt");
|
private final File mFailureReasonFile = new File("/metadata/staged-install/failure_reason.txt");
|
||||||
@@ -118,6 +121,9 @@ public class StagingManager {
|
|||||||
@GuardedBy("mSuccessfulStagedSessionIds")
|
@GuardedBy("mSuccessfulStagedSessionIds")
|
||||||
private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
|
private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
|
||||||
|
|
||||||
|
@GuardedBy("mStagedApexObservers")
|
||||||
|
private final List<IStagedApexObserver> mStagedApexObservers = new ArrayList<>();
|
||||||
|
|
||||||
interface StagedSession {
|
interface StagedSession {
|
||||||
boolean isMultiPackage();
|
boolean isMultiPackage();
|
||||||
boolean isApexSession();
|
boolean isApexSession();
|
||||||
@@ -202,6 +208,18 @@ public class StagingManager {
|
|||||||
mApexManager.markBootCompleted();
|
mApexManager.markBootCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void registerStagedApexObserver(IStagedApexObserver observer) {
|
||||||
|
synchronized (mStagedApexObservers) {
|
||||||
|
mStagedApexObservers.add(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterStagedApexObserver(IStagedApexObserver observer) {
|
||||||
|
synchronized (mStagedApexObservers) {
|
||||||
|
mStagedApexObservers.remove(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the signature used to sign the container of the new apex package
|
* Validates the signature used to sign the container of the new apex package
|
||||||
*
|
*
|
||||||
@@ -840,6 +858,9 @@ public class StagingManager {
|
|||||||
// Also, cleaning up the stageDir prevents the apex from being activated.
|
// Also, cleaning up the stageDir prevents the apex from being activated.
|
||||||
Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
|
Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
|
||||||
}
|
}
|
||||||
|
if (session.containsApexSession()) {
|
||||||
|
notifyStagedApexObservers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session was successfully aborted from apexd (if required) and pre-reboot verification
|
// Session was successfully aborted from apexd (if required) and pre-reboot verification
|
||||||
@@ -1218,7 +1239,22 @@ public class StagingManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class PreRebootVerificationHandler extends Handler {
|
private void notifyStagedApexObservers() {
|
||||||
|
synchronized (mStagedApexObservers) {
|
||||||
|
for (IStagedApexObserver observer : mStagedApexObservers) {
|
||||||
|
ApexStagedEvent event = new ApexStagedEvent();
|
||||||
|
event.stagedApexModuleNames = getStagedApexModuleNames().toArray(new String[0]);
|
||||||
|
try {
|
||||||
|
observer.onApexStaged(event);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Slog.w(TAG, "Failed to contact the observer " + re.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
final class PreRebootVerificationHandler extends Handler {
|
||||||
// Hold sessions before handler gets ready to do the verification.
|
// Hold sessions before handler gets ready to do the verification.
|
||||||
private List<StagedSession> mPendingSessions;
|
private List<StagedSession> mPendingSessions;
|
||||||
private boolean mIsReady;
|
private boolean mIsReady;
|
||||||
@@ -1244,7 +1280,8 @@ public class StagingManager {
|
|||||||
private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
|
private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
|
||||||
private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
|
private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
|
||||||
private static final int MSG_PRE_REBOOT_VERIFICATION_APK = 3;
|
private static final int MSG_PRE_REBOOT_VERIFICATION_APK = 3;
|
||||||
private static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
|
@VisibleForTesting
|
||||||
|
static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
@@ -1486,6 +1523,7 @@ public class StagingManager {
|
|||||||
if (hasApex) {
|
if (hasApex) {
|
||||||
try {
|
try {
|
||||||
mApexManager.markStagedSessionReady(session.sessionId());
|
mApexManager.markStagedSessionReady(session.sessionId());
|
||||||
|
notifyStagedApexObservers();
|
||||||
} catch (PackageManagerException e) {
|
} catch (PackageManagerException e) {
|
||||||
session.setSessionFailed(e.error, e.getMessage());
|
session.setSessionFailed(e.error, e.getMessage());
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -35,9 +35,12 @@ import android.apex.ApexSessionInfo;
|
|||||||
import android.apex.ApexSessionParams;
|
import android.apex.ApexSessionParams;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.IntentSender;
|
import android.content.IntentSender;
|
||||||
|
import android.content.pm.ApexStagedEvent;
|
||||||
|
import android.content.pm.IStagedApexObserver;
|
||||||
import android.content.pm.PackageInstaller;
|
import android.content.pm.PackageInstaller;
|
||||||
import android.content.pm.PackageInstaller.SessionInfo;
|
import android.content.pm.PackageInstaller.SessionInfo;
|
||||||
import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
|
import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
|
||||||
|
import android.os.Message;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.os.storage.IStorageManager;
|
import android.os.storage.IStorageManager;
|
||||||
import android.platform.test.annotations.Presubmit;
|
import android.platform.test.annotations.Presubmit;
|
||||||
@@ -58,6 +61,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.mockito.MockitoSession;
|
import org.mockito.MockitoSession;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
@@ -647,8 +651,16 @@ public class StagingManagerTest {
|
|||||||
parentSession.setSessionReady();
|
parentSession.setSessionReady();
|
||||||
mStagingManager.createSession(parentSession);
|
mStagingManager.createSession(parentSession);
|
||||||
|
|
||||||
|
mockApexManagerGetStagedApexInfoWithSessionId();
|
||||||
|
|
||||||
|
List<String> result = mStagingManager.getStagedApexModuleNames();
|
||||||
|
assertThat(result).containsExactly("239", "123", "124");
|
||||||
|
verify(mApexManager, times(2)).getStagedApexInfos(any());
|
||||||
|
}
|
||||||
|
|
||||||
// Make mApexManager return ApexInfo with same module name as the sessionId
|
// Make mApexManager return ApexInfo with same module name as the sessionId
|
||||||
// of the parameter that was passed into it
|
// of the parameter that was passed into it
|
||||||
|
private void mockApexManagerGetStagedApexInfoWithSessionId() {
|
||||||
when(mApexManager.getStagedApexInfos(any())).thenAnswer(new Answer<ApexInfo[]>() {
|
when(mApexManager.getStagedApexInfos(any())).thenAnswer(new Answer<ApexInfo[]>() {
|
||||||
@Override
|
@Override
|
||||||
public ApexInfo[] answer(InvocationOnMock invocation) throws Throwable {
|
public ApexInfo[] answer(InvocationOnMock invocation) throws Throwable {
|
||||||
@@ -669,10 +681,6 @@ public class StagingManagerTest {
|
|||||||
return result.toArray(new ApexInfo[0]);
|
return result.toArray(new ApexInfo[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
List<String> result = mStagingManager.getStagedApexModuleNames();
|
|
||||||
assertThat(result).containsExactly("239", "123", "124");
|
|
||||||
verify(mApexManager, times(2)).getStagedApexInfos(any());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -694,6 +702,106 @@ public class StagingManagerTest {
|
|||||||
verify(mApexManager, times(2)).getStagedApexInfos(any());
|
verify(mApexManager, times(2)).getStagedApexInfos(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void registeredStagedApexObserverIsNotifiedOnPreRebootVerificationCompletion()
|
||||||
|
throws Exception {
|
||||||
|
// Register observer
|
||||||
|
IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
|
||||||
|
mStagingManager.registerStagedApexObserver(observer);
|
||||||
|
|
||||||
|
// Create one staged session and trigger end of pre-reboot verification
|
||||||
|
{
|
||||||
|
FakeStagedSession session = new FakeStagedSession(239);
|
||||||
|
session.setIsApex(true);
|
||||||
|
mStagingManager.createSession(session);
|
||||||
|
|
||||||
|
mockApexManagerGetStagedApexInfoWithSessionId();
|
||||||
|
triggerEndOfPreRebootVerification(session);
|
||||||
|
|
||||||
|
assertThat(session.isSessionReady()).isTrue();
|
||||||
|
ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
|
||||||
|
ApexStagedEvent.class);
|
||||||
|
verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
|
||||||
|
assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
|
||||||
|
new String[]{"239"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create another staged session and verify observers are notified of union
|
||||||
|
{
|
||||||
|
Mockito.clearInvocations(observer);
|
||||||
|
FakeStagedSession session = new FakeStagedSession(240);
|
||||||
|
session.setIsApex(true);
|
||||||
|
mStagingManager.createSession(session);
|
||||||
|
|
||||||
|
triggerEndOfPreRebootVerification(session);
|
||||||
|
|
||||||
|
assertThat(session.isSessionReady()).isTrue();
|
||||||
|
ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
|
||||||
|
ApexStagedEvent.class);
|
||||||
|
verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
|
||||||
|
assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
|
||||||
|
new String[]{"239", "240"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, verify that once unregistered, observer is not notified
|
||||||
|
mStagingManager.unregisterStagedApexObserver(observer);
|
||||||
|
{
|
||||||
|
Mockito.clearInvocations(observer);
|
||||||
|
FakeStagedSession session = new FakeStagedSession(241);
|
||||||
|
session.setIsApex(true);
|
||||||
|
mStagingManager.createSession(session);
|
||||||
|
|
||||||
|
triggerEndOfPreRebootVerification(session);
|
||||||
|
|
||||||
|
assertThat(session.isSessionReady()).isTrue();
|
||||||
|
verify(observer, never()).onApexStaged(any());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void registeredStagedApexObserverIsNotifiedOnSessionAbandon() throws Exception {
|
||||||
|
// Register observer
|
||||||
|
IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
|
||||||
|
mStagingManager.registerStagedApexObserver(observer);
|
||||||
|
|
||||||
|
// Create a ready session and abandon it
|
||||||
|
FakeStagedSession session = new FakeStagedSession(239);
|
||||||
|
session.setIsApex(true);
|
||||||
|
session.setSessionReady();
|
||||||
|
session.setDestroyed(true);
|
||||||
|
mStagingManager.createSession(session);
|
||||||
|
|
||||||
|
mStagingManager.abortCommittedSession(session);
|
||||||
|
|
||||||
|
assertThat(session.isSessionReady()).isTrue();
|
||||||
|
ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
|
||||||
|
ApexStagedEvent.class);
|
||||||
|
verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
|
||||||
|
assertThat(argumentCaptor.getValue().stagedApexModuleNames).hasLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void stagedApexObserverIsOnlyCalledForApexSessions() throws Exception {
|
||||||
|
IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
|
||||||
|
mStagingManager.registerStagedApexObserver(observer);
|
||||||
|
|
||||||
|
// Trigger end of pre-reboot verification
|
||||||
|
FakeStagedSession session = new FakeStagedSession(239);
|
||||||
|
mStagingManager.createSession(session);
|
||||||
|
|
||||||
|
triggerEndOfPreRebootVerification(session);
|
||||||
|
assertThat(session.isSessionReady()).isTrue();
|
||||||
|
verify(observer, never()).onApexStaged(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void triggerEndOfPreRebootVerification(StagingManager.StagedSession session) {
|
||||||
|
StagingManager.PreRebootVerificationHandler handler =
|
||||||
|
mStagingManager.mPreRebootVerificationHandler;
|
||||||
|
Message msg = handler.obtainMessage(
|
||||||
|
handler.MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session);
|
||||||
|
handler.handleMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
private StagingManager.StagedSession createSession(int sessionId, String packageName,
|
private StagingManager.StagedSession createSession(int sessionId, String packageName,
|
||||||
long committedMillis) {
|
long committedMillis) {
|
||||||
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
|
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
|
||||||
@@ -956,9 +1064,7 @@ public class StagingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyEndPreRebootVerification() {
|
public void notifyEndPreRebootVerification() {}
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void verifySession() {
|
public void verifySession() {
|
||||||
|
|||||||
Reference in New Issue
Block a user