private void performInstall(final Uri uri, final @Nullable String base_pkg) {
		final boolean is_scheme_package = SCHEME_PACKAGE.equals(uri.getScheme());
		if (is_scheme_package && base_pkg != null) throw new IllegalArgumentException("Scheme \"package\" could never be installed as split");
		final Map<String, InputStream> input_streams = new LinkedHashMap<>();
		final String stream_name = "Island[" + mInstallInfo.getCaller() + "]";
		try {
			if (is_scheme_package) {
				final String pkg = uri.getSchemeSpecificPart();
				ApplicationInfo info = Apps.of(this).getAppInfo(pkg);
				if (info == null && (info = getIntent().getParcelableExtra(InstallerExtras.EXTRA_APP_INFO)) == null) {
					Log.e(TAG, "Cannot query app info for " + pkg);
					finish();	// Do not fall-back to default package installer, since it will fail too.
					return;
				}
				// TODO: Reject forward-locked package
				input_streams.put(stream_name, new FileInputStream(info.publicSourceDir));
				if (info.splitPublicSourceDirs != null)
					for (int i = 0, num_splits = info.splitPublicSourceDirs.length; i < num_splits; i ++) {
						final String split = info.splitPublicSourceDirs[i];
						input_streams.put(SDK_INT >= O ? info.splitNames[i] : "split" + i, new FileInputStream(split));
					}
			} else input_streams.put(stream_name, requireNonNull(getContentResolver().openInputStream(uri)));
		} catch(final IOException | RuntimeException e) {		// SecurityException may be thrown by ContentResolver.openInputStream().
			Log.w(TAG, "Error opening " + uri + " for reading.\nTo launch Island app installer, " +
					"please ensure data URI is accessible by Island, either exposed by content provider or world-readable (on pre-N)", e);
			fallbackToSystemPackageInstaller("stream_error", e);	// Default system package installer may have privilege to access the content that we can't.
			for (final Map.Entry<String, InputStream> entry : input_streams.entrySet()) IoUtils.closeQuietly(entry.getValue());
			return;
		}

		final PackageInstaller installer = getPackageManager().getPackageInstaller();
		final SessionParams params = new SessionParams(base_pkg == null ? MODE_FULL_INSTALL : MODE_INHERIT_EXISTING);
		if (mInstallInfo.getAppId() != null) params.setAppPackageName(mInstallInfo.getAppId());
		if (mInstallInfo.getAppLabel() != null) params.setAppLabel(mInstallInfo.getAppLabel());
		if (mInstallInfo.getCallerUid() != Process.INVALID_UID) params.setOriginatingUid(mInstallInfo.getCallerUid());
		if (SDK_INT >= O) params.setInstallReason(PackageManager.INSTALL_REASON_USER);
		try {
			mSession = installer.openSession(mSessionId = installer.createSession(params));
			for (final Map.Entry<String, InputStream> entry : input_streams.entrySet())
				try (final OutputStream out = mSession.openWrite(entry.getKey(), 0, - 1)) {
					final byte[] buffer = new byte[STREAM_BUFFER_SIZE];
					final InputStream input = entry.getValue();
					int count;
					while ((count = input.read(buffer)) != - 1) out.write(buffer, 0, count);
					mSession.fsync(out);
				}
		} catch (final IOException | RuntimeException e) {
			Log.e(TAG, "Error preparing installation", e);
			fallbackToSystemPackageInstaller("session", e);
			return;
		} finally {
			for (final Map.Entry<String, InputStream> entry : input_streams.entrySet()) IoUtils.closeQuietly(entry.getValue());
		}

		final PendingIntent callback = AppInstallerStatusReceiver.createCallback(this, mInstallInfo, mSessionId);
		mSession.commit(callback.getIntentSender());
		mSession.close();
		mSession = null;        // Otherwise it will be abandoned in onDestroy().

		AppInstallationNotifier.onInstallStart(this, mSessionId, mInstallInfo);
		if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) setResult(Activity.RESULT_OK);

		new Handler().postDelayed(this::finish, 500);   // A slight delay to keep in foreground, for activity launch of STATUS_PENDING_USER_ACTION.
	}
@param base_pkg the base package name for split APK installation, or null for full installation.
installer/src/main/java/com/oasisfeng/island/installer/AppInstallerActivity.java