package com.android.vending.expansion.zipfile; /* * Copyright (C) 2012 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. */ //To implement APEZProvider in your application, you'll want to change //the AUTHORITY to match what you define in the manifest. import com.android.vending.expansion.zipfile.ZipResourceFile.ZipEntryRO; import android.content.ContentProvider; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ProviderInfo; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.provider.BaseColumns; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; /** * This content provider is an optional part of the library. * * <p>Most apps don't need to use this class. This defines a * ContentProvider that marshalls the data from the ZIP files through a * content provider Uri in order to provide file access for certain Android APIs * that expect Uri access to media files. * */ public abstract class APEZProvider extends ContentProvider { private ZipResourceFile mAPKExtensionFile; private boolean mInit; public static final String FILEID = BaseColumns._ID; public static final String FILENAME = "ZPFN"; public static final String ZIPFILE = "ZFIL"; public static final String MODIFICATION = "ZMOD"; public static final String CRC32 = "ZCRC"; public static final String COMPRESSEDLEN = "ZCOL"; public static final String UNCOMPRESSEDLEN = "ZUNL"; public static final String COMPRESSIONTYPE = "ZTYP"; public static final String[] ALL_FIELDS = { FILEID, FILENAME, ZIPFILE, MODIFICATION, CRC32, COMPRESSEDLEN, UNCOMPRESSEDLEN, COMPRESSIONTYPE }; public static final int FILEID_IDX = 0; public static final int FILENAME_IDX = 1; public static final int ZIPFILE_IDX = 2; public static final int MOD_IDX = 3; public static final int CRC_IDX = 4; public static final int COMPLEN_IDX = 5; public static final int UNCOMPLEN_IDX = 6; public static final int COMPTYPE_IDX = 7; public static final int[] ALL_FIELDS_INT = { FILEID_IDX, FILENAME_IDX, ZIPFILE_IDX, MOD_IDX, CRC_IDX, COMPLEN_IDX, UNCOMPLEN_IDX, COMPTYPE_IDX }; /** * This needs to match the authority in your manifest */ public abstract String getAuthority(); @Override public int delete(Uri arg0, String arg1, String[] arg2) { // TODO Auto-generated method stub return 0; } @Override public String getType(Uri uri) { return "vnd.android.cursor.item/asset"; } @Override public Uri insert(Uri uri, ContentValues values) { // TODO Auto-generated method stub return null; } static private final String NO_FILE = "N"; private boolean initIfNecessary() { if ( !mInit ) { Context ctx = getContext(); PackageManager pm = ctx.getPackageManager(); ProviderInfo pi = pm.resolveContentProvider(getAuthority(), PackageManager.GET_META_DATA); PackageInfo packInfo; try { packInfo = pm.getPackageInfo(ctx.getPackageName(), 0); } catch (NameNotFoundException e1) { e1.printStackTrace(); return false; } int patchFileVersion; int mainFileVersion; int appVersionCode = packInfo.versionCode; String[] resourceFiles = null; if ( null != pi.metaData ) { mainFileVersion = pi.metaData.getInt("mainVersion", appVersionCode); patchFileVersion = pi.metaData.getInt("patchVersion", appVersionCode); String mainFileName = pi.metaData.getString("mainFilename", NO_FILE); if ( NO_FILE != mainFileName ) { String patchFileName = pi.metaData.getString("patchFilename", NO_FILE); if ( NO_FILE != patchFileName ) { resourceFiles = new String[] { mainFileName, patchFileName }; } else { resourceFiles = new String[] { mainFileName }; } } } else { mainFileVersion = patchFileVersion = appVersionCode; } try { if ( null == resourceFiles ) { mAPKExtensionFile = APKExpansionSupport.getAPKExpansionZipFile(ctx, mainFileVersion, patchFileVersion); } else { mAPKExtensionFile = APKExpansionSupport.getResourceZipFile(resourceFiles); } mInit = true; return true; } catch (IOException e) { e.printStackTrace(); } } return false; } @Override public boolean onCreate() { return true; } @Override public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { initIfNecessary(); String path = uri.getEncodedPath(); if ( path.startsWith("/") ) { path = path.substring(1); } return mAPKExtensionFile.getAssetFileDescriptor(path); } @Override public ContentProviderResult[] applyBatch( ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { initIfNecessary(); return super.applyBatch(operations); } @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { initIfNecessary(); AssetFileDescriptor af = openAssetFile(uri, mode); if ( null != af ) { return af.getParcelFileDescriptor(); } return null; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { initIfNecessary(); // lists all of the items in the file that match ZipEntryRO[] zipEntries; if ( null == mAPKExtensionFile ) { zipEntries = new ZipEntryRO[0]; } else { zipEntries = mAPKExtensionFile.getAllEntries(); } int[] intProjection; if ( null == projection ) { intProjection = ALL_FIELDS_INT; projection = ALL_FIELDS; } else { int len = projection.length; intProjection = new int[len]; for ( int i = 0; i < len; i++ ) { if ( projection[i].equals(FILEID) ) { intProjection[i] = FILEID_IDX; } else if ( projection[i].equals(FILENAME) ) { intProjection[i] = FILENAME_IDX; } else if ( projection[i].equals(ZIPFILE) ) { intProjection[i] = ZIPFILE_IDX; } else if ( projection[i].equals(MODIFICATION) ) { intProjection[i] = MOD_IDX; } else if ( projection[i].equals(CRC32) ) { intProjection[i] = CRC_IDX; } else if ( projection[i].equals(COMPRESSEDLEN) ) { intProjection[i] = COMPLEN_IDX; } else if ( projection[i].equals(UNCOMPRESSEDLEN) ) { intProjection[i] = UNCOMPLEN_IDX; } else if ( projection[i].equals(COMPRESSIONTYPE) ) { intProjection[i] = COMPTYPE_IDX; } else { throw new RuntimeException(); } } } MatrixCursor mc = new MatrixCursor(projection, zipEntries.length); int len = intProjection.length; for ( ZipEntryRO zer : zipEntries ) { MatrixCursor.RowBuilder rb = mc.newRow(); for ( int i = 0; i < len; i++ ) { switch (intProjection[i]) { case FILEID_IDX: rb.add(i); break; case FILENAME_IDX: rb.add(zer.mFileName); break; case ZIPFILE_IDX: rb.add(zer.getZipFileName()); break; case MOD_IDX: rb.add(zer.mWhenModified); break; case CRC_IDX: rb.add(zer.mCRC32); break; case COMPLEN_IDX: rb.add(zer.mCompressedLength); break; case UNCOMPLEN_IDX: rb.add(zer.mUncompressedLength); break; case COMPTYPE_IDX: rb.add(zer.mMethod); break; } } } return mc; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // TODO Auto-generated method stub return 0; } }