Multi-Channel Packing of American Mission

Keywords: iOS Android Java Python less

I. principle

After decompressing an Android application package as a zip file package, it is found that adding an empty file to the META-INF directory does not require re-signing. Using this mechanism, the file name of the file is the channel name. This method does not require re-signature and other steps, it is very efficient.

 

Two. Method

Metro's packaging tools have been placed in the test01 file under tools:
1. Place the packaged apk in Python Tool
2. Write the required channel in Python Tool/info/channel.txt, one channel occupies one line
3. Double-click to execute the Python Tool/MultiChannelBuildTool.py file (requiring a Python environment) to generate the channel package
4. Get Channel Information: Copy ChannelUtil.java from JavaUtil file to project and call ChannelUtil.getChannel to get channel information.

package com.czt.util;

import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.preference.PreferenceManager;
import android.text.TextUtils;

public class ChannelUtil {
	
	private static final String CHANNEL_KEY = "cztchannel";
	private static final String CHANNEL_VERSION_KEY = "cztchannel_version";
	private static String mChannel;
	/**
	 * Return to the market. If the acquisition fails, return ""
	 * @param context
	 * @return
	 */
	public static String getChannel(Context context){
		return getChannel(context, "");
	}
	/**
	 * Return to the market. Return defaultChannel if acquisition fails
	 * @param context
	 * @param defaultChannel
	 * @return
	 */
	public static String getChannel(Context context, String defaultChannel) {
		//In-memory acquisition
		if(!TextUtils.isEmpty(mChannel)){
			return mChannel;
		}
		//Get in sp
		mChannel = getChannelBySharedPreferences(context);
		if(!TextUtils.isEmpty(mChannel)){
			return mChannel;
		}
		//Get from apk
		mChannel = getChannelFromApk(context, CHANNEL_KEY);
		if(!TextUtils.isEmpty(mChannel)){
			//Save spare in sp
			saveChannelBySharedPreferences(context, mChannel);
			return mChannel;
		}
		//All acquisition failures
		return defaultChannel;
    }
	/**
	 * Get version information from apk
	 * @param context
	 * @param channelKey
	 * @return
	 */
	private static String getChannelFromApk(Context context, String channelKey) {
		//Get from the apk package
        ApplicationInfo appinfo = context.getApplicationInfo();
        String sourceDir = appinfo.sourceDir;
        //The default is in meta-inf/, so you need to stitch it up again.
        String key = "META-INF/" + channelKey;
        String ret = "";
        ZipFile zipfile = null;
        try {
            zipfile = new ZipFile(sourceDir);
            Enumeration<?> entries = zipfile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = ((ZipEntry) entries.nextElement());
                String entryName = entry.getName();
                if (entryName.startsWith(key)) {
                    ret = entryName;
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (zipfile != null) {
                try {
                    zipfile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        String[] split = ret.split("_");
        String channel = "";
        if (split != null && split.length >= 2) {
        	channel = ret.substring(split[0].length() + 1);
        }
        return channel;
	}
	/**
	 * Locally saved Channel & corresponding version number
	 * @param context
	 * @param channel
	 */
	private static void saveChannelBySharedPreferences(Context context, String channel){
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
		Editor editor = sp.edit();
		editor.putString(CHANNEL_KEY, channel);
		editor.putInt(CHANNEL_VERSION_KEY, getVersionCode(context));
		editor.commit();
	}
	/**
	 * Getting channel from sp
	 * @param context
	 * @return Get an exception for null, the value in sp is invalid, and there is no value in sp
	 */
	private static String getChannelBySharedPreferences(Context context){
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
		int currentVersionCode = getVersionCode(context);
		if(currentVersionCode == -1){
			//Get errors
			return "";
		}
		int versionCodeSaved = sp.getInt(CHANNEL_VERSION_KEY, -1);
		if(versionCodeSaved == -1){
			//Version number corresponding to channel that is not stored locally
			//Exceptions to the first use or original storage version number
			return "";
		}
		if(currentVersionCode != versionCodeSaved){
			return "";
		}
		return sp.getString(CHANNEL_KEY, "");
	}
	/**
	 * Get version number from package information
	 * @param context
	 * @return
	 */
	private static int getVersionCode(Context context){
		try{
			return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
		}catch(NameNotFoundException e) {
			e.printStackTrace();
		}
		return -1;
	}
}

  

Tool download address: http://pan.baidu.com/share/link?shareid=1485267923&uk=4218015263#list/path=%2F

Advantages and disadvantages

Advantage:
This packaging method is very fast, more than 900 channels can be completed in less than a minute.

Disadvantages:
1. google will not be available if it changes the packing rules one day, making it necessary to repackage empty files in META-INF.

2. Some illegal channel merchants can easily modify channels through tools. If a channel merchant gains windfall profits through the combination of network hijacking and tampering channels, there may be huge economic losses for programmers.
 

Posted by Hades on Thu, 11 Apr 2019 15:15:31 -0700