Skip to content

[BUG] Android - only maxSimultaneouslyAssignedPieces are downloaded if PieceSelector.getNextPieces supplies subset of all pieces in torrent #227

@pvishnyakov

Description

@pvishnyakov

Let's assume we have some torrent containing N pieces and we want to download only first 5 of them using the customized PieceSelector and the following code:

	int[] piecesToDownload = new int[]{0,1,2,3,4};
        final PieceSelector pieceSelector = new PieceSelector() {
		@Override
		public IntStream getNextPieces(BitSet bitSet, PieceStatistics pieceStatistics) {
			return IntStream.of(piecesToDownload);
		}
	};

        Torrent torrent;
        final int selectedFileIndex = 0;
	final FilePrioritySkipSelector filePrioritySkipSelector =
			torrentFile -> {
				if (torrent != null) {
					return torrent.getFiles().indexOf(torrentFile) == selectedFileIndex
							? FilePriority.HIGH_PRIORITY
							: FilePriority.SKIP;
				} else return FilePriority.SKIP;
			};
	
	BtRuntime createRuntime() {
		try {
			Config config = new Config() {
				@Override
				public EncryptionPolicy getEncryptionPolicy() {
					return EncryptionPolicy.PREFER_ENCRYPTED;
				}
				@Override
				public int getNumOfHashingThreads() {
					return Runtime.getRuntime().availableProcessors();
				}
				
				@Override
				public int getAcceptorPort() {
					return 6891;
				}
			};
			
			Module dhtModule = new DHTModule(new DHTConfig() {
				@Override
				public boolean shouldUseRouterBootstrap() {
					return true;
				}
			});
			
			return BtRuntime.builder(config)
					.autoLoadModules()
					.module(dhtModule)
					.module(new UpnpPortMapperModule())
					.module(new PeerExchangeModule())
					.module(new HttpTrackerModule())
					.build();
		} catch (Exception e) {
			System.out.println("Error creating BT runtime "+e);
			return null;
		}
	}
	
	BtClient createClient(URI uri, BtRuntime runtime, Storage storage,
	                              FilePrioritySkipSelector filePrioritySkipSelector,
	                              PieceSelector pieceSelector) {
		try {
			return Bt.client(runtime)
					.storage(storage)
					.torrent(uri.toURL())
					.afterTorrentFetched(t->{torrent = t;})
					.fileSelector(filePrioritySkipSelector)
					.selector(pieceSelector)
					.stopWhenDownloaded()
					.initEagerly()
					.build();
		} catch (Exception e) {
			System.out.println("Error creating BT client "+ e);
			return null;
		}
	}

	Storage storage = new FileSystemStorage(Paths.get(context.getFilesDir().getAbsolutePath()+"/tordl"));

	void startDownload(URI uri) {
		System.out.println("Creating BT runtime...");
		BtRuntime runtime = createRuntime();
			
		System.out.println("Creating client...");
		BtClient client = createClient(uri, runtime, storage, filePrioritySkipSelector, pieceSelector);
			
		System.out.println("Starting download...");
		client.startAsync(state -> {
			final int activePeers = state.getConnectedPeers().size();
			final int piecesComplete = state.getPiecesComplete();
			System.out.println("Peers: " + activePeers
				+ "; Downloaded: " + piecesComplete + " pieces, current range: "
				+ Arrays.toString(piecesToDownload));
		}, 5000);
		System.out.println("Download started");
	}

On Android device (tested Android 9 and 11) we will not get 5 pieces, just 3 pieces (0,1,2) will be downloaded which is equivalent to default Config.maxSimultaneouslyAssignedPieces value.
If we increase maxSimultaneouslyAssignedPieces to 5 or more, all 5 pieces will be downloaded but only from single peer.
Here comes the next trouble - any IntStream of pieces provided by custom PieceSelector in Android will not be distributed between peers, in the best case (range size<=maxSimultaneouslyAssignedPieces) all pieces will be assigned to one peer and downloaded, in worst case (range size>maxSimultaneouslyAssignedPieces) only maxSimultaneouslyAssignedPieces pieces from desired range will be assigned to one peer and downloaded.

To reproduce try to download using any torrent link (for example http://d.rutor.info/download/934413):

        startDownload(URI.create("http://d.rutor.info/download/934413"));

If we try the same code on desktop (Windows), everything works as expected - any piece range supplied by custom PieceSelector will be distributed between peers and downloaded assigning up to maxSimultaneouslyAssignedPieces pieces to each peer.

If we use another PieceSelector (for example SequentialSelector) - whole torrent will be downloaded and piece distribution between peers seems to work as expected even on Android device.

What may be the problem with Android?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions