3

内部 SSL サーバーからマップ タイルを読み込もうとしています。SSL 証明書のルート オブ トラストが Android システムによって認識されません。

W/o*.o*.t*.m*.MapTileDow*(2837): MapTile をダウンロード中の IOException: /8/37/4: javax.net.ssl.SSLPeerUnverifiedException : ピア証明書がありません

私はすでにこの問題に精通しており、この優れた SO answer に基づいてアプリケーションの残りの部分で解決しました。基本的に、私は独自のものを拡張SSLSocketFactoryX509TrustManager、アプリにバンドルされている .bks ファイルから SSL 証明書の信頼のルートをロードします。セキュアな接続を確立するために、私が呼び出し((HttpsURLConnection) connection).setSSLSocketFactory(mySSLSocketFactory)、信頼のルートを持つクラスを使用して証明書が検証されます。

私の質問は、osmdroid で同じことを行うにはどうすればよいですか? XYTileSourceマップ タイルの URL、ファイル拡張子、サイズなどを設定する場所を独自に作成しています。osmdroid がマップ タイル イメージをダウンロードするための接続を作成していることがわかりますMapTileDownloader。同じ方法で SSL の問題に対処する独自の置換クラスを作成できますが、osmdroid にデフォルトの代わりにカスタム ダウンローダーを使用するように指示するにはどうすればよいですか?

4

2 に答える 2

1

コンストラクターのおかげで、osmdroid のソースを変更せずにこれが可能であることがpublic MapView(Context context, int tileSizePixels, ResourceProxy resourceProxy, MapTileProviderBase aTileProvider)わかりました。

MySSLSocketFactory(which ) のようなカスタム クラスが既にあると仮定するとextends javax.net.ssl.SSLSocketFactory、基本的なプロセスは次のようになります。

  1. MapTileDownloaderを利用する方法でダウンロードを実行するためのドロップイン置換クラスを作成しますMySSLSocketFactory。これを と呼びましょうMyTileDownloader

  2. MapTileProviderBasicカスタムをインスタンス化するためのドロップイン置換クラスを作成しますMyTileDownloader。これを と呼びましょうMyTileProvider

  3. タイル ソースを としてインスタンス化しますnew XYTileSource(カスタム クラスを記述する必要はありません)。

  4. MyTileProviderタイル ソース インスタンスでインスタンス化します。

  5. MapVewタイル プロバイダー インスタンスでインスタンス化します。


MySSLSocketFactory読者の演習として残されています。この投稿を参照してください。


MyTileDownloader次のようになります。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;

import org.osmdroid.tileprovider.MapTile;
import org.osmdroid.tileprovider.MapTileRequestState;
import org.osmdroid.tileprovider.modules.IFilesystemCache;
import org.osmdroid.tileprovider.modules.INetworkAvailablityCheck;
import org.osmdroid.tileprovider.modules.MapTileDownloader;
import org.osmdroid.tileprovider.modules.MapTileModuleProviderBase;
import org.osmdroid.tileprovider.tilesource.BitmapTileSourceBase.LowMemoryException;
import org.osmdroid.tileprovider.tilesource.ITileSource;
import org.osmdroid.tileprovider.tilesource.OnlineTileSourceBase;
import org.osmdroid.tileprovider.util.StreamUtils;

import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.Log;

/**
 * A drop-in replacement for {@link MapTileDownloader}. This loads tiles from an
 * HTTP or HTTPS server, making use of a custom {@link SSLSocketFactory} for SSL
 * peer verification.
 */
public class MyTileDownloader extends MapTileModuleProviderBase {
    private static final String TAG = "MyMapTileDownloader";

    protected OnlineTileSourceBase mTileSource;
    protected final IFilesystemCache mFilesystemCache;
    protected final INetworkAvailablityCheck mNetworkAvailablityCheck;
    protected final SSLSocketFactory mSSLSocketFactory;

    public MyTileDownloader(ITileSource pTileSource,
            IFilesystemCache pFilesystemCache,
            INetworkAvailablityCheck pNetworkAvailablityCheck,
            SSLSocketFactory pSSLSocketFactory) {
        super(4, TILE_DOWNLOAD_MAXIMUM_QUEUE_SIZE);
        setTileSource(pTileSource);
        mFilesystemCache = pFilesystemCache;
        mNetworkAvailablityCheck = pNetworkAvailablityCheck;
        mSSLSocketFactory = pSSLSocketFactory;
    }

    public ITileSource getTileSource() {
        return mTileSource;
    }

    @Override
    public void setTileSource(final ITileSource tileSource) {
        // We are only interested in OnlineTileSourceBase tile sources
        if (tileSource instanceof OnlineTileSourceBase)
            mTileSource = (OnlineTileSourceBase) tileSource;
        else
            mTileSource = null;
    }

    @Override
    public boolean getUsesDataConnection() {
        return true;
    }

    @Override
    protected String getName() {
        return "Online Tile Download Provider";
    }

    @Override
    protected String getThreadGroupName() {
        return "downloader";
    }

    @Override
    public int getMinimumZoomLevel() {
        return (mTileSource != null ? mTileSource.getMinimumZoomLevel()
                : MINIMUM_ZOOMLEVEL);
    }

    @Override
    public int getMaximumZoomLevel() {
        return (mTileSource != null ? mTileSource.getMaximumZoomLevel()
                : MAXIMUM_ZOOMLEVEL);
    }

    @Override
    protected Runnable getTileLoader() {
        return new TileLoader();
    };

    private class TileLoader extends MapTileModuleProviderBase.TileLoader {
        @Override
        public Drawable loadTile(final MapTileRequestState aState)
                throws CantContinueException {
            if (mTileSource == null)
                return null;

            InputStream in = null;
            OutputStream out = null;
            final MapTile tile = aState.getMapTile();

            try {
                if (mNetworkAvailablityCheck != null
                        && !mNetworkAvailablityCheck.getNetworkAvailable()) {
                    if (DEBUGMODE)
                        Log.d(TAG, "Skipping " + getName()
                                + " due to NetworkAvailabliltyCheck.");
                    return null;
                }

                final String tileURLString = mTileSource.getTileURLString(tile);
                if (DEBUGMODE)
                    Log.d(TAG, "Downloading Maptile from url: " + tileURLString);

                if (TextUtils.isEmpty(tileURLString))
                    return null;

                // Create an HttpURLConnection to download the tile
                URL url = new URL(tileURLString);
                HttpURLConnection connection = (HttpURLConnection) url
                        .openConnection();
                connection.setConnectTimeout(30000);
                connection.setReadTimeout(30000);

                // Use our custom SSLSocketFactory for secure connections
                if ("https".equalsIgnoreCase(url.getProtocol()))
                    ((HttpsURLConnection) connection)
                            .setSSLSocketFactory(mSSLSocketFactory);

                // Open the input stream
                in = new BufferedInputStream(connection.getInputStream(),
                        StreamUtils.IO_BUFFER_SIZE);

                // Check to see if we got success
                if (connection.getResponseCode() != 200) {
                    Log.w(TAG, "Problem downloading MapTile: " + tile
                            + " HTTP response: " + connection.getHeaderField(0));
                    return null;
                }

                // Read the tile into an in-memory byte array
                final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
                out = new BufferedOutputStream(dataStream,
                        StreamUtils.IO_BUFFER_SIZE);
                StreamUtils.copy(in, out);
                out.flush();
                final byte[] data = dataStream.toByteArray();
                final ByteArrayInputStream byteStream = new ByteArrayInputStream(
                        data);

                // Save the data to the filesystem cache
                if (mFilesystemCache != null) {
                    mFilesystemCache.saveFile(mTileSource, tile, byteStream);
                    byteStream.reset();
                }
                final Drawable result = mTileSource.getDrawable(byteStream);
                return result;

            } catch (final UnknownHostException e) {
                Log.w(TAG, "UnknownHostException downloading MapTile: " + tile
                        + " : " + e);
                throw new CantContinueException(e);

            } catch (final LowMemoryException e) {
                Log.w(TAG, "LowMemoryException downloading MapTile: " + tile
                        + " : " + e);
                throw new CantContinueException(e);

            } catch (final FileNotFoundException e) {
                Log.w(TAG, "Tile not found: " + tile + " : " + e);

            } catch (final IOException e) {
                Log.w(TAG, "IOException downloading MapTile: " + tile + " : "
                        + e);

            } catch (final Throwable e) {
                Log.e(TAG, "Error downloading MapTile: " + tile, e);

            } finally {
                StreamUtils.closeStream(in);
                StreamUtils.closeStream(out);
            }
            return null;
        }

        @Override
        protected void tileLoaded(final MapTileRequestState pState,
                final Drawable pDrawable) {
            // Don't return the tile Drawable because we'll wait for the fs
            // provider to ask for it. This prevent flickering when a load
            // of delayed downloads complete for tiles that we might not
            // even be interested in any more.
            super.tileLoaded(pState, null);
        }
    }
}

MyTileProviderこのように見えます。

MySSLSocketFactoryこのクラス内のインスタンスにアクセスする方法が必要になることに注意してください。これは、読者の演習として残されています。を使用してこれをapp.getSSLSocketFactory()行いましたappが、 はカスタム クラスのインスタンスですextends Applicationが、マイレージは異なる場合があります。

import javax.net.ssl.SSLSocketFactory;

import org.osmdroid.tileprovider.IMapTileProviderCallback;
import org.osmdroid.tileprovider.IRegisterReceiver;
import org.osmdroid.tileprovider.MapTileProviderArray;
import org.osmdroid.tileprovider.MapTileProviderBasic;
import org.osmdroid.tileprovider.modules.INetworkAvailablityCheck;
import org.osmdroid.tileprovider.modules.MapTileFileArchiveProvider;
import org.osmdroid.tileprovider.modules.MapTileFilesystemProvider;
import org.osmdroid.tileprovider.modules.NetworkAvailabliltyCheck;
import org.osmdroid.tileprovider.modules.TileWriter;
import org.osmdroid.tileprovider.tilesource.ITileSource;
import org.osmdroid.tileprovider.util.SimpleRegisterReceiver;

import android.content.Context;

/**
 * A drop-in replacement for {@link MapTileProviderBasic}. This top-level tile
 * provider implements a basic tile request chain which includes a
 * {@link MapTileFilesystemProvider} (a file-system cache), a
 * {@link MapTileFileArchiveProvider} (archive provider), and a
 * {@link MyTileDownloader} (downloads map tiles via tile source).
 */
public class MyTileProvider extends MapTileProviderArray implements
        IMapTileProviderCallback {
    public MyTileProvider(final Context pContext, final ITileSource pTileSource) {
        this(new SimpleRegisterReceiver(pContext),
                new NetworkAvailabliltyCheck(pContext), pTileSource, app
                        .getSSLSocketFactory());
    }

    protected MyTileProvider(final IRegisterReceiver pRegisterReceiver,
            final INetworkAvailablityCheck aNetworkAvailablityCheck,
            final ITileSource pTileSource,
            final SSLSocketFactory pSSLSocketFactory) {
        super(pTileSource, pRegisterReceiver);

        // Look for raw tiles on the file system
        final MapTileFilesystemProvider fileSystemProvider = new MapTileFilesystemProvider(
                pRegisterReceiver, pTileSource);
        mTileProviderList.add(fileSystemProvider);

        // Look for tile archives on the file system
        final MapTileFileArchiveProvider archiveProvider = new MapTileFileArchiveProvider(
                pRegisterReceiver, pTileSource);
        mTileProviderList.add(archiveProvider);

        // Look for raw tiles on the Internet
        final TileWriter tileWriter = new TileWriter();
        final MyTileDownloader downloaderProvider = new MyTileDownloader(
                pTileSource, tileWriter, aNetworkAvailablityCheck,
                pSSLSocketFactory);
        mTileProviderList.add(downloaderProvider);
    }
}

最終的に、インスタンス化は次のようになります。

XYTileSource tileSource = new XYTileSource("MapQuest", null, 3, 8, 256, ".jpg",
    "https://10.0.0.1/path/to/your/map/tiles/");
MapTileProviderBase tileProvider = new MyTileProvider(context, tileSource);
ResourceProxy resourceProxy = new DefaultResourceProxyImpl(context);
MapView mapView = new MapView(context, 256, resourceProxy, tileProvider);
于 2012-08-29T14:59:23.747 に答える
0

私はosmdroidを使用していませんが、ダウンローダークラスを置き換えるパブリックインターフェイスがない限り、ソースを取得してパッチを適用して構成可能にするか、独自のダウンローダークラスを使用するのが最善の策です。何らかのMapTileDownloaderインターフェースを実装している場合は、実行時にリフレクションブードゥーを実行して置き換えることができますが、未知の副作用が発生する可能性があります。

于 2012-08-29T02:37:56.547 に答える