Loading services/core/java/com/android/server/pm/ApexManager.java +9 −4 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ import android.sysprop.ApexProperties; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; import java.io.File; Loading Loading @@ -216,7 +218,8 @@ abstract class ApexManager { * An implementation of {@link ApexManager} that should be used in case device supports updating * APEX packages. */ private static class ApexManagerImpl extends ApexManager { @VisibleForTesting static class ApexManagerImpl extends ApexManager { private final IApexService mApexService; private final Context mContext; private final Object mLock = new Object(); Loading Loading @@ -260,7 +263,9 @@ abstract class ApexManager { mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { populateAllPackagesCacheIfNeeded(); // Post populateAllPackagesCacheIfNeeded to a background thread, since it's // expensive to run it in broadcast handler thread. BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded()); mContext.unregisterReceiver(this); } }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); Loading Loading @@ -332,8 +337,8 @@ abstract class ApexManager { if (!packageInfo.packageName.equals(packageName)) { continue; } if ((!matchActive || isActive(packageInfo)) && (!matchFactory || isFactory(packageInfo))) { if ((matchActive && isActive(packageInfo)) || (matchFactory && isFactory(packageInfo))) { return packageInfo; } } Loading services/core/java/com/android/server/pm/PackageManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -4279,7 +4279,7 @@ public class PackageManagerService extends IPackageManager.Stub } return generatePackageInfo(ps, flags, userId); } if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) { if ((flags & MATCH_APEX) != 0) { return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE); } } services/tests/servicestests/res/raw/apex_test.apex 0 → 100644 +311 KiB File added.No diff preview for this file type. View file services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java 0 → 100644 +298 −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. */ package com.android.server.pm; 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.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; import android.apex.ApexInfo; import android.apex.ApexSessionInfo; import android.apex.IApexService; import android.content.Context; import android.content.pm.PackageInfo; import android.os.FileUtils; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.servicestests.R; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; 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; private static final int[] TEST_CHILD_SESSION_ID = {8888, 7777}; private ApexManager mApexManager; private Context mContext; private IApexService mApexService = mock(IApexService.class); @Before public void setUp() throws RemoteException { mContext = InstrumentationRegistry.getInstrumentation().getContext(); mApexManager = new ApexManager.ApexManagerImpl(mContext, mApexService); } @Test public void testGetPackageInfo_setFlagsMatchActivePackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); final PackageInfo activePkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_ACTIVE_PACKAGE); assertThat(activePkgPi).isNotNull(); assertThat(activePkgPi.packageName).contains(TEST_APEX_PKG); final PackageInfo factoryPkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_FACTORY_PACKAGE); assertThat(factoryPkgPi).isNull(); } @Test public void testGetPackageInfo_setFlagsMatchFactoryPackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); PackageInfo factoryPkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_FACTORY_PACKAGE); assertThat(factoryPkgPi).isNotNull(); assertThat(factoryPkgPi.packageName).contains(TEST_APEX_PKG); final PackageInfo activePkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_ACTIVE_PACKAGE); assertThat(activePkgPi).isNull(); } @Test public void testGetPackageInfo_setFlagsNone() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); assertThat(mApexManager.getPackageInfo(TEST_APEX_PKG, 0)).isNull(); } @Test public void testGetActivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, true)); assertThat(mApexManager.getActivePackages()).isNotEmpty(); } @Test public void testGetActivePackages_noneActivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); assertThat(mApexManager.getActivePackages()).isEmpty(); } @Test public void testGetFactoryPackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); assertThat(mApexManager.getFactoryPackages()).isNotEmpty(); } @Test public void testGetFactoryPackages_noneFactoryPackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); assertThat(mApexManager.getFactoryPackages()).isEmpty(); } @Test public void testGetInactivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); assertThat(mApexManager.getInactivePackages()).isNotEmpty(); } @Test public void testGetInactivePackages_noneInactivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); assertThat(mApexManager.getInactivePackages()).isEmpty(); } @Test public void testIsApexPackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); assertThat(mApexManager.isApexPackage(TEST_APEX_PKG)).isTrue(); } @Test public void testIsApexSupported() { assertThat(mApexManager.isApexSupported()).isTrue(); } @Test public void testGetStagedSessionInfo() throws RemoteException { when(mApexService.getStagedSessionInfo(anyInt())).thenReturn( getFakeStagedSessionInfo()); mApexManager.getStagedSessionInfo(TEST_SESSION_ID); verify(mApexService, times(1)).getStagedSessionInfo(TEST_SESSION_ID); } @Test public void testGetStagedSessionInfo_unKnownStagedSessionId() throws RemoteException { when(mApexService.getStagedSessionInfo(anyInt())).thenReturn( getFakeUnknownSessionInfo()); assertThat(mApexManager.getStagedSessionInfo(TEST_SESSION_ID)).isNull(); } @Test public void testSubmitStagedSession_throwPackageManagerException() throws RemoteException { doAnswer(invocation -> { throw new Exception(); }).when(mApexService).submitStagedSession(anyInt(), any(), any()); assertThrows(PackageManagerException.class, () -> mApexManager.submitStagedSession(TEST_SESSION_ID, TEST_CHILD_SESSION_ID)); } @Test public void testSubmitStagedSession_throwRunTimeException() throws RemoteException { doThrow(RemoteException.class).when(mApexService).submitStagedSession(anyInt(), any(), any()); assertThrows(RuntimeException.class, () -> mApexManager.submitStagedSession(TEST_SESSION_ID, TEST_CHILD_SESSION_ID)); } @Test public void testMarkStagedSessionReady_throwPackageManagerException() throws RemoteException { doAnswer(invocation -> { throw new Exception(); }).when(mApexService).markStagedSessionReady(anyInt()); assertThrows(PackageManagerException.class, () -> mApexManager.markStagedSessionReady(TEST_SESSION_ID)); } @Test public void testMarkStagedSessionReady_throwRunTimeException() throws RemoteException { doThrow(RemoteException.class).when(mApexService).markStagedSessionReady(anyInt()); assertThrows(RuntimeException.class, () -> mApexManager.markStagedSessionReady(TEST_SESSION_ID)); } @Test public void testAbortActiveSession_remoteException() throws RemoteException { doThrow(RemoteException.class).when(mApexService).abortActiveSession(); try { assertThat(mApexManager.abortActiveSession()).isFalse(); } catch (Exception e) { throw new AssertionError("ApexManager should not raise Exception"); } } @Test public void testMarkStagedSessionSuccessful_throwRemoteException() throws RemoteException { doThrow(RemoteException.class).when(mApexService).markStagedSessionSuccessful(anyInt()); assertThrows(RuntimeException.class, () -> mApexManager.markStagedSessionSuccessful(TEST_SESSION_ID)); } @Test public void testUninstallApex_throwException_returnFalse() throws RemoteException { doAnswer(invocation -> { throw new Exception(); }).when(mApexService).unstagePackages(any()); assertThat(mApexManager.uninstallApex(TEST_APEX_PKG)).isFalse(); } private ApexInfo[] createApexInfo(boolean isActive, boolean isFactory) { File apexFile = copyRawResourceToFile(TEST_APEX_PKG, R.raw.apex_test); ApexInfo apexInfo = new ApexInfo(); apexInfo.isActive = isActive; apexInfo.isFactory = isFactory; apexInfo.moduleName = TEST_APEX_PKG; apexInfo.modulePath = apexFile.getPath(); apexInfo.versionCode = 191000070; return new ApexInfo[]{apexInfo}; } private ApexSessionInfo getFakeStagedSessionInfo() { ApexSessionInfo stagedSessionInfo = new ApexSessionInfo(); stagedSessionInfo.sessionId = TEST_SESSION_ID; stagedSessionInfo.isStaged = true; return stagedSessionInfo; } private ApexSessionInfo getFakeUnknownSessionInfo() { ApexSessionInfo stagedSessionInfo = new ApexSessionInfo(); stagedSessionInfo.sessionId = TEST_SESSION_ID; stagedSessionInfo.isUnknown = true; return stagedSessionInfo; } /** * Copies a specified {@code resourceId} to a temp file. Returns a non-null file if the copy * succeeded */ File copyRawResourceToFile(String baseName, int resourceId) { File outFile; try { outFile = File.createTempFile(baseName, ".apex"); } catch (IOException e) { throw new AssertionError("CreateTempFile IOException" + e); } try (InputStream is = mContext.getResources().openRawResource(resourceId); FileOutputStream os = new FileOutputStream(outFile)) { assertThat(FileUtils.copy(is, os)).isGreaterThan(0L); } catch (FileNotFoundException e) { throw new AssertionError("File not found exception " + e); } catch (IOException e) { throw new AssertionError("IOException" + e); } return outFile; } } Loading
services/core/java/com/android/server/pm/ApexManager.java +9 −4 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ import android.sysprop.ApexProperties; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; import java.io.File; Loading Loading @@ -216,7 +218,8 @@ abstract class ApexManager { * An implementation of {@link ApexManager} that should be used in case device supports updating * APEX packages. */ private static class ApexManagerImpl extends ApexManager { @VisibleForTesting static class ApexManagerImpl extends ApexManager { private final IApexService mApexService; private final Context mContext; private final Object mLock = new Object(); Loading Loading @@ -260,7 +263,9 @@ abstract class ApexManager { mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { populateAllPackagesCacheIfNeeded(); // Post populateAllPackagesCacheIfNeeded to a background thread, since it's // expensive to run it in broadcast handler thread. BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded()); mContext.unregisterReceiver(this); } }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); Loading Loading @@ -332,8 +337,8 @@ abstract class ApexManager { if (!packageInfo.packageName.equals(packageName)) { continue; } if ((!matchActive || isActive(packageInfo)) && (!matchFactory || isFactory(packageInfo))) { if ((matchActive && isActive(packageInfo)) || (matchFactory && isFactory(packageInfo))) { return packageInfo; } } Loading
services/core/java/com/android/server/pm/PackageManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -4279,7 +4279,7 @@ public class PackageManagerService extends IPackageManager.Stub } return generatePackageInfo(ps, flags, userId); } if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) { if ((flags & MATCH_APEX) != 0) { return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE); } }
services/tests/servicestests/res/raw/apex_test.apex 0 → 100644 +311 KiB File added.No diff preview for this file type. View file
services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java 0 → 100644 +298 −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. */ package com.android.server.pm; 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.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; import android.apex.ApexInfo; import android.apex.ApexSessionInfo; import android.apex.IApexService; import android.content.Context; import android.content.pm.PackageInfo; import android.os.FileUtils; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.servicestests.R; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; 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; private static final int[] TEST_CHILD_SESSION_ID = {8888, 7777}; private ApexManager mApexManager; private Context mContext; private IApexService mApexService = mock(IApexService.class); @Before public void setUp() throws RemoteException { mContext = InstrumentationRegistry.getInstrumentation().getContext(); mApexManager = new ApexManager.ApexManagerImpl(mContext, mApexService); } @Test public void testGetPackageInfo_setFlagsMatchActivePackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); final PackageInfo activePkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_ACTIVE_PACKAGE); assertThat(activePkgPi).isNotNull(); assertThat(activePkgPi.packageName).contains(TEST_APEX_PKG); final PackageInfo factoryPkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_FACTORY_PACKAGE); assertThat(factoryPkgPi).isNull(); } @Test public void testGetPackageInfo_setFlagsMatchFactoryPackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); PackageInfo factoryPkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_FACTORY_PACKAGE); assertThat(factoryPkgPi).isNotNull(); assertThat(factoryPkgPi.packageName).contains(TEST_APEX_PKG); final PackageInfo activePkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_ACTIVE_PACKAGE); assertThat(activePkgPi).isNull(); } @Test public void testGetPackageInfo_setFlagsNone() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); assertThat(mApexManager.getPackageInfo(TEST_APEX_PKG, 0)).isNull(); } @Test public void testGetActivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, true)); assertThat(mApexManager.getActivePackages()).isNotEmpty(); } @Test public void testGetActivePackages_noneActivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); assertThat(mApexManager.getActivePackages()).isEmpty(); } @Test public void testGetFactoryPackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); assertThat(mApexManager.getFactoryPackages()).isNotEmpty(); } @Test public void testGetFactoryPackages_noneFactoryPackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); assertThat(mApexManager.getFactoryPackages()).isEmpty(); } @Test public void testGetInactivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); assertThat(mApexManager.getInactivePackages()).isNotEmpty(); } @Test public void testGetInactivePackages_noneInactivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); assertThat(mApexManager.getInactivePackages()).isEmpty(); } @Test public void testIsApexPackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); assertThat(mApexManager.isApexPackage(TEST_APEX_PKG)).isTrue(); } @Test public void testIsApexSupported() { assertThat(mApexManager.isApexSupported()).isTrue(); } @Test public void testGetStagedSessionInfo() throws RemoteException { when(mApexService.getStagedSessionInfo(anyInt())).thenReturn( getFakeStagedSessionInfo()); mApexManager.getStagedSessionInfo(TEST_SESSION_ID); verify(mApexService, times(1)).getStagedSessionInfo(TEST_SESSION_ID); } @Test public void testGetStagedSessionInfo_unKnownStagedSessionId() throws RemoteException { when(mApexService.getStagedSessionInfo(anyInt())).thenReturn( getFakeUnknownSessionInfo()); assertThat(mApexManager.getStagedSessionInfo(TEST_SESSION_ID)).isNull(); } @Test public void testSubmitStagedSession_throwPackageManagerException() throws RemoteException { doAnswer(invocation -> { throw new Exception(); }).when(mApexService).submitStagedSession(anyInt(), any(), any()); assertThrows(PackageManagerException.class, () -> mApexManager.submitStagedSession(TEST_SESSION_ID, TEST_CHILD_SESSION_ID)); } @Test public void testSubmitStagedSession_throwRunTimeException() throws RemoteException { doThrow(RemoteException.class).when(mApexService).submitStagedSession(anyInt(), any(), any()); assertThrows(RuntimeException.class, () -> mApexManager.submitStagedSession(TEST_SESSION_ID, TEST_CHILD_SESSION_ID)); } @Test public void testMarkStagedSessionReady_throwPackageManagerException() throws RemoteException { doAnswer(invocation -> { throw new Exception(); }).when(mApexService).markStagedSessionReady(anyInt()); assertThrows(PackageManagerException.class, () -> mApexManager.markStagedSessionReady(TEST_SESSION_ID)); } @Test public void testMarkStagedSessionReady_throwRunTimeException() throws RemoteException { doThrow(RemoteException.class).when(mApexService).markStagedSessionReady(anyInt()); assertThrows(RuntimeException.class, () -> mApexManager.markStagedSessionReady(TEST_SESSION_ID)); } @Test public void testAbortActiveSession_remoteException() throws RemoteException { doThrow(RemoteException.class).when(mApexService).abortActiveSession(); try { assertThat(mApexManager.abortActiveSession()).isFalse(); } catch (Exception e) { throw new AssertionError("ApexManager should not raise Exception"); } } @Test public void testMarkStagedSessionSuccessful_throwRemoteException() throws RemoteException { doThrow(RemoteException.class).when(mApexService).markStagedSessionSuccessful(anyInt()); assertThrows(RuntimeException.class, () -> mApexManager.markStagedSessionSuccessful(TEST_SESSION_ID)); } @Test public void testUninstallApex_throwException_returnFalse() throws RemoteException { doAnswer(invocation -> { throw new Exception(); }).when(mApexService).unstagePackages(any()); assertThat(mApexManager.uninstallApex(TEST_APEX_PKG)).isFalse(); } private ApexInfo[] createApexInfo(boolean isActive, boolean isFactory) { File apexFile = copyRawResourceToFile(TEST_APEX_PKG, R.raw.apex_test); ApexInfo apexInfo = new ApexInfo(); apexInfo.isActive = isActive; apexInfo.isFactory = isFactory; apexInfo.moduleName = TEST_APEX_PKG; apexInfo.modulePath = apexFile.getPath(); apexInfo.versionCode = 191000070; return new ApexInfo[]{apexInfo}; } private ApexSessionInfo getFakeStagedSessionInfo() { ApexSessionInfo stagedSessionInfo = new ApexSessionInfo(); stagedSessionInfo.sessionId = TEST_SESSION_ID; stagedSessionInfo.isStaged = true; return stagedSessionInfo; } private ApexSessionInfo getFakeUnknownSessionInfo() { ApexSessionInfo stagedSessionInfo = new ApexSessionInfo(); stagedSessionInfo.sessionId = TEST_SESSION_ID; stagedSessionInfo.isUnknown = true; return stagedSessionInfo; } /** * Copies a specified {@code resourceId} to a temp file. Returns a non-null file if the copy * succeeded */ File copyRawResourceToFile(String baseName, int resourceId) { File outFile; try { outFile = File.createTempFile(baseName, ".apex"); } catch (IOException e) { throw new AssertionError("CreateTempFile IOException" + e); } try (InputStream is = mContext.getResources().openRawResource(resourceId); FileOutputStream os = new FileOutputStream(outFile)) { assertThat(FileUtils.copy(is, os)).isGreaterThan(0L); } catch (FileNotFoundException e) { throw new AssertionError("File not found exception " + e); } catch (IOException e) { throw new AssertionError("IOException" + e); } return outFile; } }