com.github.chenxiaolong.dualbootpatcher.RomUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.github.chenxiaolong.dualbootpatcher.RomUtils.java

Source

/*
 * Copyright (C) 2014-2016  Andrew Gunnerson <andrewgunnerson@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.github.chenxiaolong.dualbootpatcher;

import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import com.github.chenxiaolong.dualbootpatcher.socket.exceptions.MbtoolCommandException;
import com.github.chenxiaolong.dualbootpatcher.socket.exceptions.MbtoolException;
import com.github.chenxiaolong.dualbootpatcher.socket.interfaces.MbtoolInterface;
import com.github.chenxiaolong.dualbootpatcher.socket.interfaces.StatBuf;
import com.squareup.picasso.Picasso;

import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import mbtool.daemon.v3.FileOpenFlag;

public class RomUtils {
    private static final String TAG = RomUtils.class.getSimpleName();

    private static RomInformation[] mBuiltinRoms;

    public static final String UNKNOWN_ID = "unknown";
    public static final String PRIMARY_ID = "primary";
    public static final String SECONDARY_ID = "dual";
    public static final String MULTI_ID_PREFIX = "multi-slot-";
    public static final String DATA_ID_PREFIX = "data-slot-";
    public static final String EXTSD_ID_PREFIX = "extsd-slot-";

    @SuppressWarnings("unused")
    public static class RomInformation implements Parcelable {
        // Mount points
        private String mSystem;
        private String mCache;
        private String mData;

        // Identifiers
        private String mId;

        private String mVersion;
        private String mBuild;

        private String mThumbnailPath;
        private String mWallpaperPath;
        private String mConfigPath;

        private String mDefaultName;
        private String mName;
        private int mImageResId;

        public RomInformation() {
        }

        protected RomInformation(Parcel in) {
            mSystem = in.readString();
            mCache = in.readString();
            mData = in.readString();
            mId = in.readString();
            mVersion = in.readString();
            mBuild = in.readString();
            mThumbnailPath = in.readString();
            mWallpaperPath = in.readString();
            mConfigPath = in.readString();
            mDefaultName = in.readString();
            mName = in.readString();
            mImageResId = in.readInt();
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(mSystem);
            dest.writeString(mCache);
            dest.writeString(mData);
            dest.writeString(mId);
            dest.writeString(mVersion);
            dest.writeString(mBuild);
            dest.writeString(mThumbnailPath);
            dest.writeString(mWallpaperPath);
            dest.writeString(mConfigPath);
            dest.writeString(mDefaultName);
            dest.writeString(mName);
            dest.writeInt(mImageResId);
        }

        @SuppressWarnings("unused")
        public static final Parcelable.Creator<RomInformation> CREATOR = new Parcelable.Creator<RomInformation>() {
            @Override
            public RomInformation createFromParcel(Parcel in) {
                return new RomInformation(in);
            }

            @Override
            public RomInformation[] newArray(int size) {
                return new RomInformation[size];
            }
        };

        @Override
        public String toString() {
            return "id=" + mId + ", system=" + mSystem + ", cache=" + mCache + ", data=" + mData + ", version="
                    + mVersion + ", build=" + mBuild + ", thumbnailPath=" + mThumbnailPath + ", wallpaperPath="
                    + mWallpaperPath + ", configPath=" + mConfigPath + ", defaultName=" + mDefaultName + ", name="
                    + mName + ", imageResId=" + mImageResId;
        }

        public String getSystemPath() {
            return mSystem;
        }

        public void setSystemPath(String system) {
            mSystem = system;
        }

        public String getCachePath() {
            return mCache;
        }

        public void setCachePath(String cache) {
            mCache = cache;
        }

        public String getDataPath() {
            return mData;
        }

        public void setDataPath(String data) {
            mData = data;
        }

        public String getId() {
            return mId;
        }

        public void setId(String id) {
            mId = id;
        }

        public String getVersion() {
            return mVersion;
        }

        public void setVersion(String version) {
            mVersion = version;
        }

        public String getBuild() {
            return mBuild;
        }

        public void setBuild(String build) {
            mBuild = build;
        }

        public String getThumbnailPath() {
            return mThumbnailPath;
        }

        public void setThumbnailPath(String thumbnailPath) {
            mThumbnailPath = thumbnailPath;
        }

        public String getWallpaperPath() {
            return mWallpaperPath;
        }

        public void setWallpaperPath(String wallpaperPath) {
            mWallpaperPath = wallpaperPath;
        }

        public String getConfigPath() {
            return mConfigPath;
        }

        public void setConfigPath(String configPath) {
            mConfigPath = configPath;
        }

        public String getDefaultName() {
            return mDefaultName;
        }

        public void setDefaultName(String defaultName) {
            mDefaultName = defaultName;
        }

        public String getName() {
            return mName != null ? mName : mDefaultName;
        }

        public void setName(String name) {
            mName = name;
        }

        public int getImageResId() {
            return mImageResId;
        }

        private void setImageResId(int imageResId) {
            mImageResId = imageResId;
        }
    }

    @Nullable
    public static RomInformation getCurrentRom(Context context, MbtoolInterface iface)
            throws IOException, MbtoolException, MbtoolCommandException {
        String id = iface.getBootedRomId();
        Log.d(TAG, "mbtool says current ROM ID is: " + id);

        for (RomInformation rom : getRoms(context, iface)) {
            if (rom.getId().equals(id)) {
                return rom;
            }
        }

        return null;
    }

    @NonNull
    public synchronized static RomInformation[] getRoms(Context context, MbtoolInterface iface)
            throws IOException, MbtoolException, MbtoolCommandException {
        RomInformation[] roms = iface.getInstalledRoms();

        for (RomInformation rom : roms) {
            rom.setThumbnailPath(
                    Environment.getExternalStorageDirectory() + "/MultiBoot/" + rom.getId() + "/thumbnail.webp");
            rom.setWallpaperPath(
                    Environment.getExternalStorageDirectory() + "/MultiBoot/" + rom.getId() + "/wallpaper.webp");
            rom.setConfigPath(
                    Environment.getExternalStorageDirectory() + "/MultiBoot/" + rom.getId() + "/config.json");
            rom.setImageResId(R.drawable.rom_android);
            rom.setDefaultName(getDefaultName(context, rom));

            loadConfig(rom);
        }

        return roms;
    }

    @NonNull
    public synchronized static RomInformation[] getBuiltinRoms(Context context) {
        if (mBuiltinRoms == null) {
            String[] ids = new String[] { "primary", "dual", "multi-slot-1", "multi-slot-2", "multi-slot-3" };

            mBuiltinRoms = new RomInformation[ids.length];
            for (int i = 0; i < ids.length; i++) {
                RomInformation rom = new RomInformation();
                rom.setId(ids[i]);
                rom.setDefaultName(getDefaultName(context, rom));
                mBuiltinRoms[i] = rom;
            }
        }

        return mBuiltinRoms;
    }

    public static void loadConfig(RomInformation info) {
        RomConfig config = RomConfig.getConfig(info.getConfigPath());
        info.setName(config.getName());
    }

    public static void saveConfig(RomInformation info) {
        RomConfig config = RomConfig.getConfig(info.getConfigPath());

        config.setId(info.getId());
        config.setName(info.getName());

        try {
            config.commit();
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Failed to save ROM config", e);
        }
    }

    private static String getDefaultName(Context context, RomInformation info) {
        if (info.getId().equals(PRIMARY_ID)) {
            return context.getString(R.string.primary);
        } else if (info.getId().equals(SECONDARY_ID)) {
            return context.getString(R.string.secondary);
        } else if (info.getId().startsWith(MULTI_ID_PREFIX)) {
            String num = info.getId().substring(MULTI_ID_PREFIX.length());
            return context.getString(R.string.multislot, num);
        } else if (info.getId().startsWith(DATA_ID_PREFIX)) {
            String id = info.getId().substring(DATA_ID_PREFIX.length());
            return context.getString(R.string.dataslot, id);
        } else if (info.getId().startsWith(EXTSD_ID_PREFIX)) {
            String id = info.getId().substring(EXTSD_ID_PREFIX.length());
            return context.getString(R.string.extsdslot, id);
        }

        return UNKNOWN_ID;
    }

    public static String getBootImagePath(String romId) {
        return Environment.getExternalStorageDirectory() + File.separator + "MultiBoot" + File.separator + romId
                + File.separator + "boot.img";
    }

    @NonNull
    public static String getDeviceCodename(Context context) {
        return SystemPropertiesProxy.get(context, "ro.patcher.device", Build.DEVICE);
    }

    private static boolean usesLiveWallpaper(RomInformation info, MbtoolInterface iface)
            throws IOException, MbtoolException, MbtoolCommandException {
        String wallpaperInfoPath = info.getDataPath() + "/system/users/0/wallpaper_info.xml";

        int id = -1;

        try {
            id = iface.fileOpen(wallpaperInfoPath, new short[] { FileOpenFlag.RDONLY }, 0);

            StatBuf sb = iface.fileStat(id);

            // Check file size
            if (sb.st_size < 0 || sb.st_size > 1024) {
                return false;
            }

            // Read file into memory
            byte[] data = new byte[(int) sb.st_size];
            int nWritten = 0;
            while (nWritten < data.length) {
                ByteBuffer newData = iface.fileRead(id, 10240);

                int nRead = newData.limit() - newData.position();
                newData.get(data, nWritten, nRead);
                nWritten += nRead;
            }

            iface.fileClose(id);
            id = -1;

            String xml = new String(data, Charsets.UTF_8);
            return xml.contains("component=");
        } finally {
            if (id >= 0) {
                try {
                    iface.fileClose(id);
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
    }

    public enum CacheWallpaperResult {
        UP_TO_DATE, UPDATED, FAILED, USES_LIVE_WALLPAPER
    }

    public static CacheWallpaperResult cacheWallpaper(Context context, RomInformation info, MbtoolInterface iface)
            throws IOException, MbtoolException, MbtoolCommandException {
        if (usesLiveWallpaper(info, iface)) {
            // We can't render a snapshot of a live wallpaper
            return CacheWallpaperResult.USES_LIVE_WALLPAPER;
        }

        String wallpaperPath = info.getDataPath() + "/system/users/0/wallpaper";
        File wallpaperCacheFile = new File(info.getWallpaperPath());
        FileOutputStream fos = null;

        int id = -1;

        try {
            id = iface.fileOpen(wallpaperPath, new short[] {}, 0);

            // Check if we need to re-cache the file
            StatBuf sb = iface.fileStat(id);

            if (wallpaperCacheFile.exists() && wallpaperCacheFile.lastModified() / 1000 > sb.st_mtime) {
                Log.d(TAG, "Wallpaper for " + info.getId() + " has not been changed");
                return CacheWallpaperResult.UP_TO_DATE;
            }

            // Ignore large wallpapers
            if (sb.st_size < 0 || sb.st_size > 20 * 1024 * 1024) {
                return CacheWallpaperResult.FAILED;
            }

            // Read file into memory
            byte[] data = new byte[(int) sb.st_size];
            int nWritten = 0;
            while (nWritten < data.length) {
                ByteBuffer newData = iface.fileRead(id, 10240);

                int nRead = newData.limit() - newData.position();
                newData.get(data, nWritten, nRead);
                nWritten += nRead;
            }

            iface.fileClose(id);
            id = -1;

            fos = new FileOutputStream(wallpaperCacheFile);

            // Compression can be very slow (more than 10 seconds) for a large wallpaper, so we'll
            // just cache the actual file instead
            fos.write(data);

            // Load into bitmap
            //Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            //if (bitmap == null) {
            //    return false;
            //}
            //bitmap.compress(Bitmap.CompressFormat.WEBP, 100, fos);
            //bitmap.recycle();

            // Invalidate picasso cache
            Picasso.with(context).invalidate(wallpaperCacheFile);

            Log.d(TAG, "Wallpaper for " + info.getId() + " has been cached");
            return CacheWallpaperResult.UPDATED;
        } finally {
            if (id >= 0) {
                try {
                    iface.fileClose(id);
                } catch (IOException e) {
                    // Ignore
                }
            }
            IOUtils.closeQuietly(fos);
        }
    }
}