Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6a435500 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add a flag for whether non-system apps can be installed on internal."

parents d6817254 199b5d6e
Loading
Loading
Loading
Loading
+105 −23
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.internal.content;

import static android.net.TrafficStats.MB_IN_BYTES;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -38,7 +36,7 @@ import android.provider.Settings;
import android.util.ArraySet;
import android.util.Log;

import libcore.io.IoUtils;
import com.android.internal.annotations.VisibleForTesting;

import java.io.File;
import java.io.FileOutputStream;
@@ -50,6 +48,11 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import libcore.io.IoUtils;

import static android.net.TrafficStats.MB_IN_BYTES;
import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;

/**
 * Constants used internally between the PackageManager
 * and media container service transports.
@@ -74,6 +77,8 @@ public class PackageHelper {
    public static final int APP_INSTALL_INTERNAL = 1;
    public static final int APP_INSTALL_EXTERNAL = 2;

    private static TestableInterface sDefaultTestableInterface = null;

    public static IStorageManager getStorageManager() throws RemoteException {
        IBinder service = ServiceManager.getService("mount");
        if (service != null) {
@@ -337,6 +342,65 @@ public class PackageHelper {
        return false;
    }

    /**
     * A group of external dependencies used in
     * {@link #resolveInstallVolume(Context, String, int, long)}. It can be backed by real values
     * from the system or mocked ones for testing purposes.
     */
    public static abstract class TestableInterface {
        abstract public StorageManager getStorageManager(Context context);
        abstract public boolean getForceAllowOnExternalSetting(Context context);
        abstract public boolean getAllow3rdPartyOnInternalConfig(Context context);
        abstract public ApplicationInfo getExistingAppInfo(Context context, String packageName);
        abstract public File getDataDirectory();

        public boolean fitsOnInternalStorage(Context context, long sizeBytes) {
            StorageManager storage = getStorageManager(context);
            File target = getDataDirectory();
            return (sizeBytes <= storage.getStorageBytesUntilLow(target));
        }
    }

    private synchronized static TestableInterface getDefaultTestableInterface() {
        if (sDefaultTestableInterface == null) {
            sDefaultTestableInterface = new TestableInterface() {
                @Override
                public StorageManager getStorageManager(Context context) {
                    return context.getSystemService(StorageManager.class);
                }

                @Override
                public boolean getForceAllowOnExternalSetting(Context context) {
                    return Settings.Global.getInt(context.getContentResolver(),
                            Settings.Global.FORCE_ALLOW_ON_EXTERNAL, 0) != 0;
                }

                @Override
                public boolean getAllow3rdPartyOnInternalConfig(Context context) {
                    return context.getResources().getBoolean(
                            com.android.internal.R.bool.config_allow3rdPartyAppOnInternal);
                }

                @Override
                public ApplicationInfo getExistingAppInfo(Context context, String packageName) {
                    ApplicationInfo existingInfo = null;
                    try {
                        existingInfo = context.getPackageManager().getApplicationInfo(packageName,
                                PackageManager.MATCH_ANY_USER);
                    } catch (NameNotFoundException ignored) {
                    }
                    return existingInfo;
                }

                @Override
                public File getDataDirectory() {
                    return Environment.getDataDirectory();
                }
            };
        }
        return sDefaultTestableInterface;
    }

    /**
     * Given a requested {@link PackageInfo#installLocation} and calculated
     * install size, pick the actual volume to install the app. Only considers
@@ -348,25 +412,44 @@ public class PackageHelper {
     */
    public static String resolveInstallVolume(Context context, String packageName,
            int installLocation, long sizeBytes) throws IOException {
        final boolean forceAllowOnExternal = Settings.Global.getInt(
                context.getContentResolver(), Settings.Global.FORCE_ALLOW_ON_EXTERNAL, 0) != 0;
        TestableInterface testableInterface = getDefaultTestableInterface();
        return resolveInstallVolume(context, packageName,
                installLocation, sizeBytes, testableInterface);
    }

    @VisibleForTesting
    public static String resolveInstallVolume(Context context, String packageName,
            int installLocation, long sizeBytes, TestableInterface testInterface)
            throws IOException {
        final boolean forceAllowOnExternal = testInterface.getForceAllowOnExternalSetting(context);
        final boolean allow3rdPartyOnInternal =
                testInterface.getAllow3rdPartyOnInternalConfig(context);
        // TODO: handle existing apps installed in ASEC; currently assumes
        // they'll end up back on internal storage
        ApplicationInfo existingInfo = null;
        try {
            existingInfo = context.getPackageManager().getApplicationInfo(packageName,
                    PackageManager.MATCH_ANY_USER);
        } catch (NameNotFoundException ignored) {
        }
        ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context, packageName);

        final StorageManager storageManager = context.getSystemService(StorageManager.class);
        final boolean fitsOnInternal = fitsOnInternal(context, sizeBytes);
        final boolean fitsOnInternal = testInterface.fitsOnInternalStorage(context, sizeBytes);
        final StorageManager storageManager =
                testInterface.getStorageManager(context);

        // System apps always forced to internal storage
        if (existingInfo != null && existingInfo.isSystemApp()) {
            if (fitsOnInternal) {
                return StorageManager.UUID_PRIVATE_INTERNAL;
            } else {
                throw new IOException("Not enough space on existing volume "
                        + existingInfo.volumeUuid + " for system app " + packageName + " upgrade");
            }
        }

        // Now deal with non-system apps.
        final ArraySet<String> allCandidates = new ArraySet<>();
        VolumeInfo bestCandidate = null;
        long bestCandidateAvailBytes = Long.MIN_VALUE;
        for (VolumeInfo vol : storageManager.getVolumes()) {
            if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
            boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id);
            if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()
                    && (!isInternalStorage || allow3rdPartyOnInternal)) {
                final long availBytes = storageManager.getStorageBytesUntilLow(new File(vol.path));
                if (availBytes >= sizeBytes) {
                    allCandidates.add(vol.fsUuid);
@@ -378,11 +461,6 @@ public class PackageHelper {
            }
        }

        // System apps always forced to internal storage
        if (existingInfo != null && existingInfo.isSystemApp()) {
            installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
        }

        // If app expresses strong desire for internal storage, honor it
        if (!forceAllowOnExternal
                && installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
@@ -391,6 +469,11 @@ public class PackageHelper {
                throw new IOException("Cannot automatically move " + packageName + " from "
                        + existingInfo.volumeUuid + " to internal storage");
            }

            if (!allow3rdPartyOnInternal) {
                throw new IOException("Not allowed to install non-system apps on internal storage");
            }

            if (fitsOnInternal) {
                return StorageManager.UUID_PRIVATE_INTERNAL;
            } else {
@@ -411,14 +494,13 @@ public class PackageHelper {
            }
        }

        // We're left with either preferring external or auto, so just pick
        // We're left with new installations with either preferring external or auto, so just pick
        // volume with most space
        if (bestCandidate != null) {
            return bestCandidate.fsUuid;
        } else if (fitsOnInternal) {
            return StorageManager.UUID_PRIVATE_INTERNAL;
        } else {
            throw new IOException("No special requests, but no room anywhere");
            throw new IOException("No special requests, but no room on allowed volumes. "
                + " allow3rdPartyOnInternal? " + allow3rdPartyOnInternal);
        }
    }

+3 −0
Original line number Diff line number Diff line
@@ -2690,4 +2690,7 @@
         user-set value if toggled by settings so the "Transition animation scale" setting
         should also be hidden if intended to be permanent. -->
    <item name="config_appTransitionAnimationDurationScaleDefault" format="float" type="dimen">1.0</item>

    <!-- Flag indicates that whether non-system apps can be installed on internal storage. -->
    <bool name="config_allow3rdPartyAppOnInternal">true</bool>
</resources>
+4 −1
Original line number Diff line number Diff line
@@ -2756,4 +2756,7 @@

  <!-- Network Recommendation -->
  <java-symbol type="array" name="config_networkRecommendationPackageNames" />

  <!-- Whether allow 3rd party apps on internal storage. -->
  <java-symbol type="bool" name="config_allow3rdPartyAppOnInternal" />
</resources>
+425 −2

File changed.

Preview size limit exceeded, changes collapsed.