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

Commit f36d53cb authored by Svet Ganov's avatar Svet Ganov Committed by Svetoslav Ganov
Browse files

Prune unused static libs and instant apps if space needed

We are caching unused static shared libs and instant apps
(installed and uninstalled) opportunistically. If space is
needed we delete these to free up space.

Test: manual

bug:62045000

Change-Id: Id992dee5c7c6e36b8e8b81050602dbc4eeafb0f9
parent ddd54580
Loading
Loading
Loading
Loading
+39 −3
Original line number Diff line number Diff line
@@ -9817,13 +9817,49 @@ public final class Settings {
        public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature";

        /**
         * The duration for caching uninstalled instant apps.
         * The min period for caching installed instant apps in milliseconds.
         * <p>
         * Type: long
         * @hide
         */
        public static final String UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS =
                "uninstalled_instant_app_cache_duration_millis";
        public static final String INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
                "installed_instant_app_min_cache_period";

        /**
         * The max period for caching installed instant apps in milliseconds.
         * <p>
         * Type: long
         * @hide
         */
        public static final String INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
                "installed_instant_app_max_cache_period";

        /**
         * The min period for caching uninstalled instant apps in milliseconds.
         * <p>
         * Type: long
         * @hide
         */
        public static final String UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
                "uninstalled_instant_app_min_cache_period";

        /**
         * The max period for caching uninstalled instant apps in milliseconds.
         * <p>
         * Type: long
         * @hide
         */
        public static final String UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
                "uninstalled_instant_app_max_cache_period";

        /**
         * The min period for caching unused static shared libs in milliseconds.
         * <p>
         * Type: long
         * @hide
         */
        public static final String UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
                "unused_static_shared_lib_min_cache_period";

        /**
         * Allows switching users when system user is locked.
+5 −1
Original line number Diff line number Diff line
@@ -310,7 +310,7 @@ message GlobalSettingsProto {
    SettingProto lte_service_forced = 265;
    SettingProto ephemeral_cookie_max_size_bytes = 266;
    SettingProto enable_ephemeral_feature = 267;
    SettingProto uninstalled_ephemeral_app_cache_duration_millis = 268;
    SettingProto installed_instant_app_min_cache_period = 268;
    SettingProto allow_user_switching_when_system_user_locked = 269;
    SettingProto boot_count = 270;
    SettingProto safe_boot_disallowed = 271;
@@ -331,6 +331,10 @@ message GlobalSettingsProto {
    SettingProto network_recommendations_package = 286;
    SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287;
    SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288;
    SettingProto installed_instant_app_max_cache_period = 289;
    SettingProto uninstalled_instant_app_min_cache_period = 290;
    SettingProto uninstalled_instant_app_max_cache_period = 291;
    SettingProto unused_static_shared_lib_min_cache_period = 292;
}

message SecureSettingsProto {
+5 −1
Original line number Diff line number Diff line
@@ -334,7 +334,11 @@ public class SettingsBackupTest {
                    Settings.Global.TRUSTED_SOUND,
                    Settings.Global.TZINFO_UPDATE_CONTENT_URL,
                    Settings.Global.TZINFO_UPDATE_METADATA_URL,
                    Settings.Global.UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS,
                    Settings.Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
                    Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
                    Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
                    Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
                    Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
                    Settings.Global.UNLOCK_SOUND,
                    Settings.Global.USE_GOOGLE_MAIL,
                    Settings.Global.VT_IMS_ENABLED,
+14 −2
Original line number Diff line number Diff line
@@ -888,8 +888,20 @@ class SettingsProtoDumpUtil {
                Settings.Global.ENABLE_EPHEMERAL_FEATURE,
                GlobalSettingsProto.ENABLE_EPHEMERAL_FEATURE);
        dumpSetting(s, p,
                Settings.Global.UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS,
                GlobalSettingsProto.UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS);
                Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
                GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
        dumpSetting(s, p,
                Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
                GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
        dumpSetting(s, p,
                Settings.Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
                GlobalSettingsProto.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
        dumpSetting(s, p,
                Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
                GlobalSettingsProto.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
        dumpSetting(s, p,
                Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
                GlobalSettingsProto.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD);
        dumpSetting(s, p,
                Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED,
                GlobalSettingsProto.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED);
+191 −30
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Intent;
import android.content.pm.InstantAppInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -32,6 +33,8 @@ import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.AtomicFile;
@@ -76,7 +79,16 @@ class InstantAppRegistry {

    private static final String LOG_TAG = "InstantAppRegistry";

    private static final long DEFAULT_UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS =
    static final long DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
            DEBUG ? 30 * 1000L /* thirty seconds */ : 7 * 24 * 60 * 60 * 1000L; /* one week */

    private static final long DEFAULT_INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
            DEBUG ? 60 * 1000L /* one min */ : 6 * 30 * 24 * 60 * 60 * 1000L; /* six months */

    static final long DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
            DEBUG ? 30 * 1000L /* thirty seconds */ : 7 * 24 * 60 * 60 * 1000L; /* one week */

    private static final long DEFAULT_UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
            DEBUG ? 60 * 1000L /* one min */ : 6 * 30 * 24 * 60 * 60 * 1000L; /* six months */

    private static final String INSTANT_APPS_FOLDER = "instant";
@@ -535,18 +547,161 @@ class InstantAppRegistry {
        }
    }

    public void pruneInstantAppsLPw() {
        // For now we prune only state for uninstalled instant apps
        final long maxCacheDurationMillis = Settings.Global.getLong(
    void pruneInstantApps() {
        final long maxInstalledCacheDuration = Settings.Global.getLong(
                mService.mContext.getContentResolver(),
                Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
                DEFAULT_INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);

        final long maxUninstalledCacheDuration = Settings.Global.getLong(
                mService.mContext.getContentResolver(),
                Settings.Global.UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS,
                DEFAULT_UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS);
                Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
                DEFAULT_UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);

        try {
            pruneInstantApps(Long.MAX_VALUE,
                    maxInstalledCacheDuration, maxUninstalledCacheDuration);
        } catch (IOException e) {
            Slog.e(LOG_TAG, "Error pruning installed and uninstalled instant apps", e);
        }
    }

    boolean pruneInstalledInstantApps(long neededSpace, long maxInstalledCacheDuration) {
        try {
            return pruneInstantApps(neededSpace, maxInstalledCacheDuration, Long.MAX_VALUE);
        } catch (IOException e) {
            Slog.e(LOG_TAG, "Error pruning installed instant apps", e);
            return false;
        }
    }

    boolean pruneUninstalledInstantApps(long neededSpace, long maxUninstalledCacheDuration) {
        try {
            return pruneInstantApps(neededSpace, Long.MAX_VALUE, maxUninstalledCacheDuration);
        } catch (IOException e) {
            Slog.e(LOG_TAG, "Error pruning uninstalled instant apps", e);
            return false;
        }
    }

    /**
     * Prunes instant apps until there is enough <code>neededSpace</code>. Both
     * installed and uninstalled instant apps are pruned that are older than
     * <code>maxInstalledCacheDuration</code> and <code>maxUninstalledCacheDuration</code>
     * respectively. All times are in milliseconds.
     *
     * @param neededSpace The space to ensure is free.
     * @param maxInstalledCacheDuration The max duration for caching installed apps in millis.
     * @param maxUninstalledCacheDuration The max duration for caching uninstalled apps in millis.
     * @return Whether enough space was freed.
     *
     * @throws IOException
     */
    private boolean pruneInstantApps(long neededSpace, long maxInstalledCacheDuration,
            long maxUninstalledCacheDuration) throws IOException {
        final StorageManager storage = mService.mContext.getSystemService(StorageManager.class);
        final File file = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);

        if (file.getUsableSpace() >= neededSpace) {
            return true;
        }

        List<String> packagesToDelete = null;

        final int[] allUsers;
        final long now = System.currentTimeMillis();

        // Prune first installed instant apps
        synchronized (mService.mPackages) {
            allUsers = PackageManagerService.sUserManager.getUserIds();

            final int packageCount = mService.mPackages.size();
            for (int i = 0; i < packageCount; i++) {
                final PackageParser.Package pkg = mService.mPackages.valueAt(i);
                if (now - pkg.getLatestPackageUseTimeInMills() < maxInstalledCacheDuration) {
                    continue;
                }
                if (!(pkg.mExtras instanceof PackageSetting)) {
                    continue;
                }
                final PackageSetting  ps = (PackageSetting) pkg.mExtras;
                boolean installedOnlyAsInstantApp = false;
                for (int userId : allUsers) {
                    if (ps.getInstalled(userId)) {
                        if (ps.getInstantApp(userId)) {
                            installedOnlyAsInstantApp = true;
                        } else {
                            installedOnlyAsInstantApp = false;
                            break;
                        }
                    }
                }
                if (installedOnlyAsInstantApp) {
                    if (packagesToDelete == null) {
                        packagesToDelete = new ArrayList<>();
                    }
                    packagesToDelete.add(pkg.packageName);
                }
            }

            if (packagesToDelete != null) {
                packagesToDelete.sort((String lhs, String rhs) -> {
                    final PackageParser.Package lhsPkg = mService.mPackages.get(lhs);
                    final PackageParser.Package rhsPkg = mService.mPackages.get(rhs);
                    if (lhsPkg == null && rhsPkg == null) {
                        return 0;
                    } else if (lhsPkg == null) {
                        return -1;
                    } else if (rhsPkg == null) {
                        return 1;
                    } else {
                        if (lhsPkg.getLatestPackageUseTimeInMills() >
                                rhsPkg.getLatestPackageUseTimeInMills()) {
                            return 1;
                        } else if (lhsPkg.getLatestPackageUseTimeInMills() <
                                rhsPkg.getLatestPackageUseTimeInMills()) {
                            return -1;
                        } else {
                            if (lhsPkg.mExtras instanceof PackageSetting
                                    && rhsPkg.mExtras instanceof PackageSetting) {
                                final PackageSetting lhsPs = (PackageSetting) lhsPkg.mExtras;
                                final PackageSetting rhsPs = (PackageSetting) rhsPkg.mExtras;
                                if (lhsPs.firstInstallTime > rhsPs.firstInstallTime) {
                                    return 1;
                                } else {
                                    return -1;
                                }
                            } else {
                                return 0;
                            }
                        }
                    }
                });
            }
        }

        if (packagesToDelete != null) {
            final int packageCount = packagesToDelete.size();
            for (int i = 0; i < packageCount; i++) {
                final String packageToDelete = packagesToDelete.get(i);
                if (mService.deletePackageX(packageToDelete, PackageManager.VERSION_CODE_HIGHEST,
                        UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS)
                                == PackageManager.DELETE_SUCCEEDED) {
                    if (file.getUsableSpace() >= neededSpace) {
                        return true;
                    }
                }
            }
        }

        // Prune uninstalled instant apps
        synchronized (mService.mPackages) {
            // TODO: Track last used time for uninstalled instant apps for better pruning
            for (int userId : UserManagerService.getInstance().getUserIds()) {
                // Prune in-memory state
                removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) -> {
                    final long elapsedCachingMillis = System.currentTimeMillis() - state.mTimestamp;
                return (elapsedCachingMillis > maxCacheDurationMillis);
                    return (elapsedCachingMillis > maxUninstalledCacheDuration);
                }, userId);

                // Prune on-disk state
@@ -570,12 +725,18 @@ class InstantAppRegistry {

                    final long elapsedCachingMillis = System.currentTimeMillis()
                            - metadataFile.lastModified();
                if (elapsedCachingMillis > maxCacheDurationMillis) {
                    if (elapsedCachingMillis > maxUninstalledCacheDuration) {
                        deleteDir(instantDir);
                        if (file.getUsableSpace() >= neededSpace) {
                            return true;
                        }
                    }
                }
            }
        }

        return false;
    }

    private @Nullable List<InstantAppInfo> getInstalledInstantApplicationsLPr(
            @UserIdInt int userId) {
Loading