Loading core/java/android/app/ApplicationPackageManager.java +36 −11 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.annotation.XmlRes; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; Loading Loading @@ -1386,7 +1387,7 @@ public class ApplicationPackageManager extends PackageManager { } } ApplicationPackageManager(ContextImpl context, protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) { mContext = context; mPM = pm; Loading Loading @@ -1820,6 +1821,12 @@ public class ApplicationPackageManager extends PackageManager { @Override public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) { final StorageManager storage = mContext.getSystemService(StorageManager.class); return getPackageCurrentVolume(app, storage); } @VisibleForTesting protected @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app, StorageManager storage) { if (app.isInternal()) { return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); } else if (app.isExternalAsec()) { Loading @@ -1831,25 +1838,43 @@ public class ApplicationPackageManager extends PackageManager { @Override public @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) { final StorageManager storage = mContext.getSystemService(StorageManager.class); final VolumeInfo currentVol = getPackageCurrentVolume(app); final List<VolumeInfo> vols = storage.getVolumes(); final StorageManager storageManager = mContext.getSystemService(StorageManager.class); return getPackageCandidateVolumes(app, storageManager, mPM); } @VisibleForTesting protected @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app, StorageManager storageManager, IPackageManager pm) { final VolumeInfo currentVol = getPackageCurrentVolume(app, storageManager); final List<VolumeInfo> vols = storageManager.getVolumes(); final List<VolumeInfo> candidates = new ArrayList<>(); for (VolumeInfo vol : vols) { if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(mContext, app, vol)) { if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(mContext, app, vol, pm)) { candidates.add(vol); } } return candidates; } private boolean isPackageCandidateVolume( ContextImpl context, ApplicationInfo app, VolumeInfo vol) { final boolean forceAllowOnExternal = Settings.Global.getInt( @VisibleForTesting protected boolean isForceAllowOnExternal(Context context) { return Settings.Global.getInt( context.getContentResolver(), Settings.Global.FORCE_ALLOW_ON_EXTERNAL, 0) != 0; // Private internal is always an option } @VisibleForTesting protected boolean isAllow3rdPartyOnInternal(Context context) { return context.getResources().getBoolean( com.android.internal.R.bool.config_allow3rdPartyAppOnInternal); } private boolean isPackageCandidateVolume( ContextImpl context, ApplicationInfo app, VolumeInfo vol, IPackageManager pm) { final boolean forceAllowOnExternal = isForceAllowOnExternal(context); if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { return true; return app.isSystemApp() || isAllow3rdPartyOnInternal(context); } // System apps and apps demanding internal storage can't be moved Loading @@ -1875,7 +1900,7 @@ public class ApplicationPackageManager extends PackageManager { // Some apps can't be moved. (e.g. device admins) try { if (mPM.isPackageDeviceAdminOnAnyUser(app.packageName)) { if (pm.isPackageDeviceAdminOnAnyUser(app.packageName)) { return false; } } catch (RemoteException e) { Loading core/java/android/content/pm/PackageManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -1359,6 +1359,14 @@ public abstract class PackageManager { */ public static final int MOVE_FAILED_DEVICE_ADMIN = -8; /** * Error code that is passed to the {@link IPackageMoveObserver} if system does not allow * non-system apps to be moved to internal storage. * * @hide */ public static final int MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL = -9; /** * Flag parameter for {@link #movePackage} to indicate that * the package should be moved to internal storage if its Loading core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java 0 → 100644 +242 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 android.app; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import junit.framework.TestCase; import org.mockito.Mockito; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static android.os.storage.VolumeInfo.STATE_MOUNTED; import static android.os.storage.VolumeInfo.STATE_UNMOUNTED; public class ApplicationPackageManagerTest extends TestCase { private static final String sInternalVolPath = "/data"; private static final String sAdoptedVolPath = "/mnt/expand/123"; private static final String sPublicVolPath = "/emulated"; private static final String sPrivateUnmountedVolPath = "/private"; private static final String sInternalVolUuid = null; //StorageManager.UUID_PRIVATE_INTERNAL private static final String sAdoptedVolUuid = "adopted"; private static final String sPublicVolUuid = "emulated"; private static final String sPrivateUnmountedVolUuid = "private"; private static final VolumeInfo sInternalVol = new VolumeInfo("private", VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/); private static final VolumeInfo sAdoptedVol = new VolumeInfo("adopted", VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/); private static final VolumeInfo sPublicVol = new VolumeInfo("public", VolumeInfo.TYPE_PUBLIC, null /*DiskInfo*/, null /*partGuid*/); private static final VolumeInfo sPrivateUnmountedVol = new VolumeInfo("private2", VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/); private static final List<VolumeInfo> sVolumes = new ArrayList<>(); static { sInternalVol.path = sInternalVolPath; sInternalVol.state = STATE_MOUNTED; sInternalVol.fsUuid = sInternalVolUuid; sAdoptedVol.path = sAdoptedVolPath; sAdoptedVol.state = STATE_MOUNTED; sAdoptedVol.fsUuid = sAdoptedVolUuid; sPublicVol.state = STATE_MOUNTED; sPublicVol.path = sPublicVolPath; sPublicVol.fsUuid = sPublicVolUuid; sPrivateUnmountedVol.state = STATE_UNMOUNTED; sPrivateUnmountedVol.path = sPrivateUnmountedVolPath; sPrivateUnmountedVol.fsUuid = sPrivateUnmountedVolUuid; sVolumes.add(sInternalVol); sVolumes.add(sAdoptedVol); sVolumes.add(sPublicVol); sVolumes.add(sPrivateUnmountedVol); } private static final class MockedApplicationPackageManager extends ApplicationPackageManager { private boolean mForceAllowOnExternal = false; private boolean mAllow3rdPartyOnInternal = true; public MockedApplicationPackageManager() { super(null, null); } public void setForceAllowOnExternal(boolean forceAllowOnExternal) { mForceAllowOnExternal = forceAllowOnExternal; } public void setAllow3rdPartyOnInternal(boolean allow3rdPartyOnInternal) { mAllow3rdPartyOnInternal = allow3rdPartyOnInternal; } @Override public boolean isForceAllowOnExternal(Context context) { return mForceAllowOnExternal; } @Override public boolean isAllow3rdPartyOnInternal(Context context) { return mAllow3rdPartyOnInternal; } } private StorageManager getMockedStorageManager() { StorageManager storageManager = Mockito.mock(StorageManager.class); Mockito.when(storageManager.getVolumes()).thenReturn(sVolumes); Mockito.when(storageManager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL)) .thenReturn(sInternalVol); Mockito.when(storageManager.findVolumeByUuid(sAdoptedVolUuid)) .thenReturn(sAdoptedVol); Mockito.when(storageManager.findVolumeByUuid(sPublicVolUuid)) .thenReturn(sPublicVol); Mockito.when(storageManager.findVolumeByUuid(sPrivateUnmountedVolUuid)) .thenReturn(sPrivateUnmountedVol); return storageManager; } private void verifyReturnedVolumes(List<VolumeInfo> actualVols, VolumeInfo... exptectedVols) { boolean failed = false; if (actualVols.size() != exptectedVols.length) { failed = true; } else { for (VolumeInfo vol : exptectedVols) { if (!actualVols.contains(vol)) { failed = true; break; } } } if (failed) { fail("Wrong volumes returned.\n Expected: " + Arrays.toString(exptectedVols) + "\n Actual: " + Arrays.toString(actualVols.toArray())); } } public void testGetCandidateVolumes_systemApp() throws Exception { ApplicationInfo sysAppInfo = new ApplicationInfo(); sysAppInfo.flags = ApplicationInfo.FLAG_SYSTEM; StorageManager storageManager = getMockedStorageManager(); IPackageManager pm = Mockito.mock(IPackageManager.class); MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager(); appPkgMgr.setAllow3rdPartyOnInternal(true); appPkgMgr.setForceAllowOnExternal(true); List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); appPkgMgr.setAllow3rdPartyOnInternal(true); appPkgMgr.setForceAllowOnExternal(false); candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); appPkgMgr.setAllow3rdPartyOnInternal(false); appPkgMgr.setForceAllowOnExternal(false); candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); appPkgMgr.setAllow3rdPartyOnInternal(false); appPkgMgr.setForceAllowOnExternal(true); candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); } public void testGetCandidateVolumes_3rdParty_internalOnly() throws Exception { ApplicationInfo appInfo = new ApplicationInfo(); StorageManager storageManager = getMockedStorageManager(); IPackageManager pm = Mockito.mock(IPackageManager.class); Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false); MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager(); // must allow 3rd party on internal, otherwise the app wouldn't have been installed before. appPkgMgr.setAllow3rdPartyOnInternal(true); // INSTALL_LOCATION_INTERNAL_ONLY AND INSTALL_LOCATION_UNSPECIFIED are treated the same. int[] locations = {PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY, PackageInfo.INSTALL_LOCATION_UNSPECIFIED}; for (int location : locations) { appInfo.installLocation = location; appPkgMgr.setForceAllowOnExternal(true); List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes( appInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol, sAdoptedVol); appPkgMgr.setForceAllowOnExternal(false); candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); } } public void testGetCandidateVolumes_3rdParty_auto() throws Exception { ApplicationInfo appInfo = new ApplicationInfo(); StorageManager storageManager = getMockedStorageManager(); IPackageManager pm = Mockito.mock(IPackageManager.class); MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager(); appPkgMgr.setForceAllowOnExternal(true); // INSTALL_LOCATION_AUTO AND INSTALL_LOCATION_PREFER_EXTERNAL are treated the same. int[] locations = {PackageInfo.INSTALL_LOCATION_AUTO, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL}; for (int location : locations) { appInfo.installLocation = location; appInfo.flags = 0; appInfo.volumeUuid = sInternalVolUuid; Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false); appPkgMgr.setAllow3rdPartyOnInternal(true); List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes( appInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol, sAdoptedVol); appInfo.volumeUuid = sInternalVolUuid; appPkgMgr.setAllow3rdPartyOnInternal(true); Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(true); candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); appInfo.flags = ApplicationInfo.FLAG_EXTERNAL_STORAGE; appInfo.volumeUuid = sAdoptedVolUuid; appPkgMgr.setAllow3rdPartyOnInternal(false); candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm); verifyReturnedVolumes(candidates, sAdoptedVol); } } } services/core/java/com/android/server/pm/PackageManagerService.java +9 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY; import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.content.pm.PackageManager.MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL; import static android.content.pm.PackageManager.MOVE_FAILED_DEVICE_ADMIN; import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST; import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR; Loading Loading @@ -21042,6 +21043,14 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); "Cannot move system application"); } final boolean isInternalStorage = VolumeInfo.ID_PRIVATE_INTERNAL.equals(volumeUuid); final boolean allow3rdPartyOnInternal = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allow3rdPartyAppOnInternal); if (isInternalStorage && !allow3rdPartyOnInternal) { throw new PackageManagerException(MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL, "3rd party apps are not allowed on internal storage"); } if (pkg.applicationInfo.isExternalAsec()) { currentAsec = true; currentVolumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL; Loading
core/java/android/app/ApplicationPackageManager.java +36 −11 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.annotation.XmlRes; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; Loading Loading @@ -1386,7 +1387,7 @@ public class ApplicationPackageManager extends PackageManager { } } ApplicationPackageManager(ContextImpl context, protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) { mContext = context; mPM = pm; Loading Loading @@ -1820,6 +1821,12 @@ public class ApplicationPackageManager extends PackageManager { @Override public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) { final StorageManager storage = mContext.getSystemService(StorageManager.class); return getPackageCurrentVolume(app, storage); } @VisibleForTesting protected @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app, StorageManager storage) { if (app.isInternal()) { return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); } else if (app.isExternalAsec()) { Loading @@ -1831,25 +1838,43 @@ public class ApplicationPackageManager extends PackageManager { @Override public @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) { final StorageManager storage = mContext.getSystemService(StorageManager.class); final VolumeInfo currentVol = getPackageCurrentVolume(app); final List<VolumeInfo> vols = storage.getVolumes(); final StorageManager storageManager = mContext.getSystemService(StorageManager.class); return getPackageCandidateVolumes(app, storageManager, mPM); } @VisibleForTesting protected @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app, StorageManager storageManager, IPackageManager pm) { final VolumeInfo currentVol = getPackageCurrentVolume(app, storageManager); final List<VolumeInfo> vols = storageManager.getVolumes(); final List<VolumeInfo> candidates = new ArrayList<>(); for (VolumeInfo vol : vols) { if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(mContext, app, vol)) { if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(mContext, app, vol, pm)) { candidates.add(vol); } } return candidates; } private boolean isPackageCandidateVolume( ContextImpl context, ApplicationInfo app, VolumeInfo vol) { final boolean forceAllowOnExternal = Settings.Global.getInt( @VisibleForTesting protected boolean isForceAllowOnExternal(Context context) { return Settings.Global.getInt( context.getContentResolver(), Settings.Global.FORCE_ALLOW_ON_EXTERNAL, 0) != 0; // Private internal is always an option } @VisibleForTesting protected boolean isAllow3rdPartyOnInternal(Context context) { return context.getResources().getBoolean( com.android.internal.R.bool.config_allow3rdPartyAppOnInternal); } private boolean isPackageCandidateVolume( ContextImpl context, ApplicationInfo app, VolumeInfo vol, IPackageManager pm) { final boolean forceAllowOnExternal = isForceAllowOnExternal(context); if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { return true; return app.isSystemApp() || isAllow3rdPartyOnInternal(context); } // System apps and apps demanding internal storage can't be moved Loading @@ -1875,7 +1900,7 @@ public class ApplicationPackageManager extends PackageManager { // Some apps can't be moved. (e.g. device admins) try { if (mPM.isPackageDeviceAdminOnAnyUser(app.packageName)) { if (pm.isPackageDeviceAdminOnAnyUser(app.packageName)) { return false; } } catch (RemoteException e) { Loading
core/java/android/content/pm/PackageManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -1359,6 +1359,14 @@ public abstract class PackageManager { */ public static final int MOVE_FAILED_DEVICE_ADMIN = -8; /** * Error code that is passed to the {@link IPackageMoveObserver} if system does not allow * non-system apps to be moved to internal storage. * * @hide */ public static final int MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL = -9; /** * Flag parameter for {@link #movePackage} to indicate that * the package should be moved to internal storage if its Loading
core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java 0 → 100644 +242 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 android.app; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import junit.framework.TestCase; import org.mockito.Mockito; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static android.os.storage.VolumeInfo.STATE_MOUNTED; import static android.os.storage.VolumeInfo.STATE_UNMOUNTED; public class ApplicationPackageManagerTest extends TestCase { private static final String sInternalVolPath = "/data"; private static final String sAdoptedVolPath = "/mnt/expand/123"; private static final String sPublicVolPath = "/emulated"; private static final String sPrivateUnmountedVolPath = "/private"; private static final String sInternalVolUuid = null; //StorageManager.UUID_PRIVATE_INTERNAL private static final String sAdoptedVolUuid = "adopted"; private static final String sPublicVolUuid = "emulated"; private static final String sPrivateUnmountedVolUuid = "private"; private static final VolumeInfo sInternalVol = new VolumeInfo("private", VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/); private static final VolumeInfo sAdoptedVol = new VolumeInfo("adopted", VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/); private static final VolumeInfo sPublicVol = new VolumeInfo("public", VolumeInfo.TYPE_PUBLIC, null /*DiskInfo*/, null /*partGuid*/); private static final VolumeInfo sPrivateUnmountedVol = new VolumeInfo("private2", VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/); private static final List<VolumeInfo> sVolumes = new ArrayList<>(); static { sInternalVol.path = sInternalVolPath; sInternalVol.state = STATE_MOUNTED; sInternalVol.fsUuid = sInternalVolUuid; sAdoptedVol.path = sAdoptedVolPath; sAdoptedVol.state = STATE_MOUNTED; sAdoptedVol.fsUuid = sAdoptedVolUuid; sPublicVol.state = STATE_MOUNTED; sPublicVol.path = sPublicVolPath; sPublicVol.fsUuid = sPublicVolUuid; sPrivateUnmountedVol.state = STATE_UNMOUNTED; sPrivateUnmountedVol.path = sPrivateUnmountedVolPath; sPrivateUnmountedVol.fsUuid = sPrivateUnmountedVolUuid; sVolumes.add(sInternalVol); sVolumes.add(sAdoptedVol); sVolumes.add(sPublicVol); sVolumes.add(sPrivateUnmountedVol); } private static final class MockedApplicationPackageManager extends ApplicationPackageManager { private boolean mForceAllowOnExternal = false; private boolean mAllow3rdPartyOnInternal = true; public MockedApplicationPackageManager() { super(null, null); } public void setForceAllowOnExternal(boolean forceAllowOnExternal) { mForceAllowOnExternal = forceAllowOnExternal; } public void setAllow3rdPartyOnInternal(boolean allow3rdPartyOnInternal) { mAllow3rdPartyOnInternal = allow3rdPartyOnInternal; } @Override public boolean isForceAllowOnExternal(Context context) { return mForceAllowOnExternal; } @Override public boolean isAllow3rdPartyOnInternal(Context context) { return mAllow3rdPartyOnInternal; } } private StorageManager getMockedStorageManager() { StorageManager storageManager = Mockito.mock(StorageManager.class); Mockito.when(storageManager.getVolumes()).thenReturn(sVolumes); Mockito.when(storageManager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL)) .thenReturn(sInternalVol); Mockito.when(storageManager.findVolumeByUuid(sAdoptedVolUuid)) .thenReturn(sAdoptedVol); Mockito.when(storageManager.findVolumeByUuid(sPublicVolUuid)) .thenReturn(sPublicVol); Mockito.when(storageManager.findVolumeByUuid(sPrivateUnmountedVolUuid)) .thenReturn(sPrivateUnmountedVol); return storageManager; } private void verifyReturnedVolumes(List<VolumeInfo> actualVols, VolumeInfo... exptectedVols) { boolean failed = false; if (actualVols.size() != exptectedVols.length) { failed = true; } else { for (VolumeInfo vol : exptectedVols) { if (!actualVols.contains(vol)) { failed = true; break; } } } if (failed) { fail("Wrong volumes returned.\n Expected: " + Arrays.toString(exptectedVols) + "\n Actual: " + Arrays.toString(actualVols.toArray())); } } public void testGetCandidateVolumes_systemApp() throws Exception { ApplicationInfo sysAppInfo = new ApplicationInfo(); sysAppInfo.flags = ApplicationInfo.FLAG_SYSTEM; StorageManager storageManager = getMockedStorageManager(); IPackageManager pm = Mockito.mock(IPackageManager.class); MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager(); appPkgMgr.setAllow3rdPartyOnInternal(true); appPkgMgr.setForceAllowOnExternal(true); List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); appPkgMgr.setAllow3rdPartyOnInternal(true); appPkgMgr.setForceAllowOnExternal(false); candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); appPkgMgr.setAllow3rdPartyOnInternal(false); appPkgMgr.setForceAllowOnExternal(false); candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); appPkgMgr.setAllow3rdPartyOnInternal(false); appPkgMgr.setForceAllowOnExternal(true); candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); } public void testGetCandidateVolumes_3rdParty_internalOnly() throws Exception { ApplicationInfo appInfo = new ApplicationInfo(); StorageManager storageManager = getMockedStorageManager(); IPackageManager pm = Mockito.mock(IPackageManager.class); Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false); MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager(); // must allow 3rd party on internal, otherwise the app wouldn't have been installed before. appPkgMgr.setAllow3rdPartyOnInternal(true); // INSTALL_LOCATION_INTERNAL_ONLY AND INSTALL_LOCATION_UNSPECIFIED are treated the same. int[] locations = {PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY, PackageInfo.INSTALL_LOCATION_UNSPECIFIED}; for (int location : locations) { appInfo.installLocation = location; appPkgMgr.setForceAllowOnExternal(true); List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes( appInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol, sAdoptedVol); appPkgMgr.setForceAllowOnExternal(false); candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); } } public void testGetCandidateVolumes_3rdParty_auto() throws Exception { ApplicationInfo appInfo = new ApplicationInfo(); StorageManager storageManager = getMockedStorageManager(); IPackageManager pm = Mockito.mock(IPackageManager.class); MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager(); appPkgMgr.setForceAllowOnExternal(true); // INSTALL_LOCATION_AUTO AND INSTALL_LOCATION_PREFER_EXTERNAL are treated the same. int[] locations = {PackageInfo.INSTALL_LOCATION_AUTO, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL}; for (int location : locations) { appInfo.installLocation = location; appInfo.flags = 0; appInfo.volumeUuid = sInternalVolUuid; Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false); appPkgMgr.setAllow3rdPartyOnInternal(true); List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes( appInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol, sAdoptedVol); appInfo.volumeUuid = sInternalVolUuid; appPkgMgr.setAllow3rdPartyOnInternal(true); Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(true); candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm); verifyReturnedVolumes(candidates, sInternalVol); appInfo.flags = ApplicationInfo.FLAG_EXTERNAL_STORAGE; appInfo.volumeUuid = sAdoptedVolUuid; appPkgMgr.setAllow3rdPartyOnInternal(false); candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm); verifyReturnedVolumes(candidates, sAdoptedVol); } } }
services/core/java/com/android/server/pm/PackageManagerService.java +9 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY; import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.content.pm.PackageManager.MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL; import static android.content.pm.PackageManager.MOVE_FAILED_DEVICE_ADMIN; import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST; import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR; Loading Loading @@ -21042,6 +21043,14 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); "Cannot move system application"); } final boolean isInternalStorage = VolumeInfo.ID_PRIVATE_INTERNAL.equals(volumeUuid); final boolean allow3rdPartyOnInternal = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allow3rdPartyAppOnInternal); if (isInternalStorage && !allow3rdPartyOnInternal) { throw new PackageManagerException(MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL, "3rd party apps are not allowed on internal storage"); } if (pkg.applicationInfo.isExternalAsec()) { currentAsec = true; currentVolumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;