在开发Open VPN应用程序时出现以下消息: 07-04 21:33:34.998 20337-21875 / pioneers.safwat.myvpn6 E / AndroidRuntime:致命异常:OpenVPNManagementThread java.lang.UnsatisfiedLinkError:找不到本机方法:pioneers.safwat.myvpn6.NativeUtils.jniclose:(I)V at pioneers.safwat.myvpn6.NativeUtils.jniclose(原生方法) 在pioneers.safwat.myvpn6.OpenVpnManagementThread.protectFileDescriptor(OpenVpnManagementThread.java:175) 在pioneers.safwat.myvpn6.OpenVpnManagementThread.processNeedCommand(OpenVpnManagementThread.java:387) 在pioneers.safwat.myvpn6.OpenVpnManagementThread.processCommand(OpenVpnManagementThread.java:227) 在pioneers.safwat.myvpn6.OpenVpnManagementThread.processInput(OpenVpnManagementThread.java:198) 在pioneers.safwat.myvpn6.OpenVpnManagementThread.run(OpenVpnManagementThread.java:146) 在java.lang.Thread.run(Thread.java:841) 07-04 21:33:35.288 20337-20337 / pioneers.safwat.myvpn6 E / ActivityThread:Activity pioneers.safwat.myvpn6.MainActivity_vpn已泄露最初绑定的ServiceConnection pioneers.safwat.myvpn6.MainActivity_vpn$1@42537e00 android.app.ServiceConnectionLeaked:Activity pioneers.safwat.myvpn6.MainActivity_vpn已泄露最初绑定的ServiceConnection pioneers.safwat.myvpn6.MainActivity_vpn$1@42537e00 在android.app.LoadedApk $ ServiceDispatcher。(LoadedApk.java:979) 在android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:873) 在android.app.ContextImpl.bindServiceAsUser(ContextImpl.java:1850) 在android.app.ContextImpl.bindService(ContextImpl.java:1838) 在android.content.ContextWrapper.bindService(ContextWrapper.java:503) 在pioneers.safwat.myvpn6.MainActivity_vpn.onCreate(MainActivity_vpn.java:53) 在android.app.Activity.performCreate(Activity.java:5372) 在android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1104) 在android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2270)
Nativeutilis类:
public class NativeUtils {
public static native byte[] rsasign(byte[] input,int pkey) throws InvalidKeyException;
public static native String[] getIfconfig() throws IllegalArgumentException;
static native void jniclose(int fdint);
static {
// System.loadLibrary("opencv_java");
System.loadLibrary("stlport_shared");
System.loadLibrary("opvpnutil");
System.loadLibrary("openvpn");
System.loadLibrary("ssl");
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN)
System.loadLibrary("crypto");
}
}
OpenVpnManagementThread类:
public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
private static final String TAG = "Durai";
private LocalSocket mSocket;
private VpnProfile mProfile;
private OpenVPNService mOpenVPNService;
private LinkedList<FileDescriptor> mFDList = new LinkedList<FileDescriptor>();
private LocalServerSocket mServerSocket;
private boolean mReleaseHold = true;
private boolean mWaitingForRelease = false;
private long mLastHoldRelease = 0;
private static final Vector<OpenVpnManagementThread> active = new Vector<OpenVpnManagementThread>();
private LocalSocket mServerSocketLocal;
private pauseReason lastPauseReason = pauseReason.noNetwork;
public OpenVpnManagementThread(VpnProfile profile, OpenVPNService openVpnService) {
mProfile = profile;
mOpenVPNService = openVpnService;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(openVpnService);
boolean managemeNetworkState = prefs.getBoolean("netchangereconnect", true);
if (managemeNetworkState)
mReleaseHold = false;
}
public boolean openManagementInterface(@NotNull Context c) {
// Could take a while to open connection
int tries = 8;
String socketName = (c.getCacheDir().getAbsolutePath() + "/" + "mgmtsocket");
// The mServerSocketLocal is transferred to the LocalServerSocket, ignore warning
mServerSocketLocal = new LocalSocket();
while (tries > 0 && !mServerSocketLocal.isConnected()) {
try {
mServerSocketLocal.bind(new LocalSocketAddress(socketName,
LocalSocketAddress.Namespace.FILESYSTEM));
} catch (IOException e) {
// wait 300 ms before retrying
try {
Thread.sleep(300);
} catch (InterruptedException e1) {
}
}
tries--;
}
try {
mServerSocket = new LocalServerSocket(mServerSocketLocal.getFileDescriptor());
return true;
} catch (IOException e) {
VpnStatus.logException(e);
}
return false;
}
public void managmentCommand(String cmd) {
try {
if (mSocket != null && mSocket.getOutputStream() != null) {
mSocket.getOutputStream().write(cmd.getBytes());
mSocket.getOutputStream().flush();
}
} catch (IOException e) {
// Ignore socket stack traces
}
}
@Override
public void run() {
byte[] buffer = new byte[2048];
// mSocket.setSoTimeout(5); // Setting a timeout cannot be that bad
String pendingInput = "";
synchronized (active) {
active.add(this);
}
try {
// Wait for a client to connect
mSocket = mServerSocket.accept();
InputStream instream = mSocket.getInputStream();
// Close the management socket after client connected
mServerSocket.close();
// Closing one of the two sockets also closes the other
//mServerSocketLocal.close();
while (true) {
int numbytesread = instream.read(buffer);
if (numbytesread == -1)
return;
FileDescriptor[] fds = null;
try {
fds = mSocket.getAncillaryFileDescriptors();
} catch (IOException e) {
VpnStatus.logException("Error reading fds from socket", e);
}
if (fds != null) {
Collections.addAll(mFDList, fds);
}
String input = new String(buffer, 0, numbytesread, "UTF-8");
pendingInput += input;
pendingInput = processInput(pendingInput);
}
} catch (IOException e) {
if (!e.getMessage().equals("socket closed") && !e.getMessage().equals("Connection reset by peer"))
VpnStatus.logException(e);
}
synchronized (active) {
active.remove(this);
}
}
//! Hack O Rama 2000!
private void protectFileDescriptor(FileDescriptor fd) {
Exception exp;
try {
Method getInt = FileDescriptor.class.getDeclaredMethod("getInt$");
int fdint = (Integer) getInt.invoke(fd);
boolean result = mOpenVPNService.protect(fdint);
if (!result)
VpnStatus.logWarning("Could not protect VPN socket");
NativeUtils.jniclose(fdint);
return;
} catch (NoSuchMethodException e) {
exp = e;
} catch (IllegalArgumentException e) {
exp = e;
} catch (IllegalAccessException e) {
exp = e;
} catch (InvocationTargetException e) {
exp = e;
} catch (NullPointerException e) {
exp = e;
}
Log.d("Openvpn", "Failed to retrieve fd from socket: " + fd);
VpnStatus.logException("Failed to retrieve fd from socket (" + fd + ")", exp);
}
private String processInput(String pendingInput) {
while (pendingInput.contains("\n")) {
String[] tokens = pendingInput.split("\\r?\\n", 2);
processCommand(tokens[0]);
if (tokens.length == 1)
// No second part, newline was at the end
pendingInput = "";
else
pendingInput = tokens[1];
}
return pendingInput;
}
private void processCommand(String command) {
//Log.i(TAG, "Line from managment" + command);
if (command.startsWith(">") && command.contains(":")) {
String[] parts = command.split(":", 2);
String cmd = parts[0].substring(1);
String argument = parts[1];
if (cmd.equals("INFO")) {
/* Ignore greeting from management */
return;
} else if (cmd.equals("PASSWORD")) {
processPWCommand(argument);
} else if (cmd.equals("HOLD")) {
handleHold();
} else if (cmd.equals("NEED-OK")) {
processNeedCommand(argument);
} else if (cmd.equals("BYTECOUNT")) {
processByteCount(argument);
} else if (cmd.equals("STATE")) {
processState(argument);
} else if (cmd.equals("PROXY")) {
processProxyCMD(argument);
} else if (cmd.equals("LOG")) {
processLogMessage(argument);
} else if (cmd.equals("RSA_SIGN")) {
processSignCommand(argument);
} else {
VpnStatus.logWarning("MGMT: Got unrecognized command" + command);
Log.i(TAG, "Got unrecognized command" + command);
}
} else if (command.startsWith("SUCCESS:")) {
/* Ignore this kind of message too */
return;
} else if (command.startsWith("PROTECTFD: ")) {
FileDescriptor fdtoprotect = mFDList.pollFirst();
if (fdtoprotect != null)
protectFileDescriptor(fdtoprotect);
} else {
Log.i(TAG, "Got unrecognized line from managment" + command);
VpnStatus.logWarning("MGMT: Got unrecognized line from management:" + command);
}
}
private void processLogMessage(String argument) {
String[] args = argument.split(",", 4);
Log.d("OpenVPN", argument);
VpnStatus.LogLevel level;
if (args[1].equals("I")) {
level = VpnStatus.LogLevel.INFO;
} else if (args[1].equals("W")) {
level = VpnStatus.LogLevel.WARNING;
} else if (args[1].equals("D")) {
level = VpnStatus.LogLevel.VERBOSE;
} else if (args[1].equals("F")) {
level = VpnStatus.LogLevel.ERROR;
} else {
level = VpnStatus.LogLevel.INFO;
}
int ovpnlevel = Integer.parseInt(args[2]) & 0x0F;
String msg = args[3];
if (msg.startsWith("MANAGEMENT: CMD"))
ovpnlevel = Math.max(4, ovpnlevel);
VpnStatus.logMessageOpenVPN(level, ovpnlevel, msg);
}
private void handleHold() {
if (mReleaseHold) {
releaseHoldCmd();
} else {
mWaitingForRelease = true;
VpnStatus.updateStatePause(lastPauseReason);
}
}
private void releaseHoldCmd() {
if ((System.currentTimeMillis() - mLastHoldRelease) < 5000) {
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
}
mWaitingForRelease = false;
mLastHoldRelease = System.currentTimeMillis();
managmentCommand("hold release\n");
managmentCommand("bytecount " + mBytecountInterval + "\n");
managmentCommand("state on\n");
}
public void releaseHold() {
mReleaseHold = true;
if (mWaitingForRelease)
releaseHoldCmd();
}
private void processProxyCMD(String argument) {
String[] args = argument.split(",", 3);
SocketAddress proxyaddr = ProxyDetection.detectProxy(mProfile);
if (args.length >= 2) {
String proto = args[1];
if (proto.equals("UDP")) {
proxyaddr = null;
}
}
if (proxyaddr instanceof InetSocketAddress) {
InetSocketAddress isa = (InetSocketAddress) proxyaddr;
VpnStatus.logInfo(R.string.using_proxy, isa.getHostName(), isa.getPort());
String proxycmd = String.format(Locale.ENGLISH, "proxy HTTP %s %d\n", isa.getHostName(), isa.getPort());
managmentCommand(proxycmd);
} else {
managmentCommand("proxy NONE\n");
}
}
private void processState(String argument) {
String[] args = argument.split(",", 3);
String currentstate = args[1];
if (args[2].equals(",,"))
VpnStatus.updateStateString(currentstate, "");
else
VpnStatus.updateStateString(currentstate, args[2]);
}
private void processByteCount(String argument) {
// >BYTECOUNT:{BYTES_IN},{BYTES_OUT}
int comma = argument.indexOf(',');
long in = Long.parseLong(argument.substring(0, comma));
long out = Long.parseLong(argument.substring(comma + 1));
VpnStatus.updateByteCount(in, out);
}
private void processNeedCommand(String argument) {
int p1 = argument.indexOf('\'');
int p2 = argument.indexOf('\'', p1 + 1);
String needed = argument.substring(p1 + 1, p2);
String extra = argument.split(":", 2)[1];
String status = "ok";
if (needed.equals("PROTECTFD")) {
FileDescriptor fdtoprotect = mFDList.pollFirst();
protectFileDescriptor(fdtoprotect);
} else if (needed.equals("DNSSERVER")) {
mOpenVPNService.addDNS(extra);
} else if (needed.equals("DNSDOMAIN")) {
mOpenVPNService.setDomain(extra);
} else if (needed.equals("ROUTE")) {
String[] routeparts = extra.split(" ");
if (routeparts.length == 5) {
if (BuildConfig.DEBUG) Assert.assertEquals("dev", routeparts[3]);
mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], routeparts[4]);
} else if (routeparts.length >= 3) {
mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], null);
} else {
VpnStatus.logError("Unrecognized ROUTE cmd:" + Arrays.toString(routeparts) + " | " + argument);
}
} else if (needed.equals("ROUTE6")) {
String[] routeparts = extra.split(" ");
mOpenVPNService.addRoutev6(routeparts[0], routeparts[1]);
} else if (needed.equals("IFCONFIG")) {
String[] ifconfigparts = extra.split(" ");
int mtu = Integer.parseInt(ifconfigparts[2]);
mOpenVPNService.setLocalIP(ifconfigparts[0], ifconfigparts[1], mtu, ifconfigparts[3]);
} else if (needed.equals("IFCONFIG6")) {
mOpenVPNService.setLocalIPv6(extra);
} else if (needed.equals("PERSIST_TUN_ACTION")) {
// check if tun cfg stayed the same
status = mOpenVPNService.getTunReopenStatus();
} else if (needed.equals("OPENTUN")) {
if (sendTunFD(needed, extra))
return;
else
status = "cancel";
} else {
Log.e(TAG, "Unkown needok command " + argument);
return;
}
String cmd = String.format("needok '%s' %s\n", needed, status);
managmentCommand(cmd);
}
private boolean sendTunFD(String needed, String extra) {
Exception exp;
if (!extra.equals("tun")) {
VpnStatus.logError(String.format("Device type %s requested, but only tun is possible with the Android API, sorry!", extra));
return false;
}
ParcelFileDescriptor pfd = mOpenVPNService.openTun();
if (pfd == null)
return false;
Method setInt;
int fdint = pfd.getFd();
try {
setInt = FileDescriptor.class.getDeclaredMethod("setInt$", int.class);
FileDescriptor fdtosend = new FileDescriptor();
setInt.invoke(fdtosend, fdint);
FileDescriptor[] fds = {fdtosend};
mSocket.setFileDescriptorsForSend(fds);
String cmd = String.format("needok '%s' %s\n", needed, "ok");
managmentCommand(cmd);
mSocket.setFileDescriptorsForSend(null);
pfd.close();
return true;
} catch (NoSuchMethodException e) {
exp = e;
} catch (IllegalArgumentException e) {
exp = e;
} catch (IllegalAccessException e) {
exp = e;
} catch (InvocationTargetException e) {
exp = e;
} catch (IOException e) {
exp = e;
}
VpnStatus.logException("Could not send fd over socket", exp);
return false;
}
private void processPWCommand(String argument) {
String needed;
try {
int p1 = argument.indexOf('\'');
int p2 = argument.indexOf('\'', p1 + 1);
needed = argument.substring(p1 + 1, p2);
if (argument.startsWith("Verification Failed")) {
proccessPWFailed(needed, argument.substring(p2 + 1));
return;
}
} catch (StringIndexOutOfBoundsException sioob) {
VpnStatus.logError("Could not parse management Password command: " + argument);
return;
}
String pw = null;
if (needed.equals("Private Key")) {
pw = mProfile.getPasswordPrivateKey();
} else if (needed.equals("Auth")) {
String usercmd = String.format("username '%s' %s\n",
needed, VpnProfile.openVpnEscape(mProfile.mUsername));
managmentCommand(usercmd);
pw = mProfile.getPasswordAuth();
}
if (pw != null) {
String cmd = String.format("password '%s' %s\n", needed, VpnProfile.openVpnEscape(pw));
managmentCommand(cmd);
} else {
VpnStatus.logError(String.format("Openvpn requires Authentication type '%s' but no password/key information available", needed));
}
}
private void proccessPWFailed(String needed, String args) {
VpnStatus.updateStateString("AUTH_FAILED", needed + args,R.string.state_auth_failed, VpnStatus.ConnectionStatus.LEVEL_AUTH_FAILED);
}
private static boolean stopOpenVPN() {
synchronized (active) {
boolean sendCMD = false;
for (OpenVpnManagementThread mt : active) {
mt.managmentCommand("signal SIGINT\n");
sendCMD = true;
try {
if (mt.mSocket != null)
mt.mSocket.close();
} catch (IOException e) {
}
}
return sendCMD;
}
}
@Override
public void networkChange() {
if (!mWaitingForRelease)
managmentCommand("network-change\n");
}
public void signalusr1() {
mReleaseHold = false;
if (!mWaitingForRelease)
managmentCommand("signal SIGUSR1\n");
else
VpnStatus.updateStatePause(lastPauseReason);
}
public void reconnect() {
signalusr1();
releaseHold();
}
private void processSignCommand(String b64data) {
String signed_string = mProfile.getSignedData(b64data);
if (signed_string == null) {
managmentCommand("rsa-sig\n");
managmentCommand("\nEND\n");
stopOpenVPN();
return;
}
managmentCommand("rsa-sig\n");
managmentCommand(signed_string);
managmentCommand("\nEND\n");
}
@Override
public void pause(pauseReason reason) {
lastPauseReason = reason;
signalusr1();
}
@Override
public void resume() {
releaseHold();
/* Reset the reason why we are disconnected */
lastPauseReason = pauseReason.noNetwork;
}
@Override
public boolean stopVPN() {
return stopOpenVPN();
}
}
MainActivity_vpn类:
public class MainActivity_vpn extends Activity {
protected OpenVPNService mService;
boolean mBound = false;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
OpenVPNService.LocalBinder binder = (OpenVPNService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mService =null;
mBound = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_vpn);
Intent intent = new Intent(getBaseContext(), OpenVPNService.class);
intent.setAction(OpenVPNService.START_SERVICE);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
registerReceiver(broadcastReceiver, new IntentFilter("CONNECTION_CHANGE"));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.settingsmenu, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (Constants.isVPNConnected){
// disable connect button if VPN is connected
menu.getItem(0).setEnabled(false);
// enable disconnect button if VPN is connected
menu.getItem(1).setEnabled(true);
} else{
// enable connect button if VPN is disconnected
menu.getItem(0).setEnabled(true);
// disable disconnect button if VPN is disconnected
menu.getItem(1).setEnabled(false);
}
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_startvpn:
configureAndStartVpn() ;
return true ;
case R.id.action_stopvpn:
stopVPN() ;
return true ;
case R.id.action_removeProfile:
removeProfile();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void removeProfile() {
final ProfileManager pm = ProfileManager.getInstance(MainActivity_vpn.this) ;
final VpnProfile profile = pm.getProfileByName(Constants.VPN_PROFILE_NAME) ;
if (profile != null) {
stopVPN() ;
pm.removeProfile(getApplicationContext(),profile);
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(MainActivity_vpn.this,"The VPN Configuration is deleted", duration);
toast.show();
} else {
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(MainActivity_vpn.this,"There are no VPN Configurations to delete", duration);toast.show();
}
}
private void stopVPN() {
try{
ProfileManager.setConntectedVpnProfileDisconnected(MainActivity_vpn.this);
if(mService.getManagement()!=null)
mService.getManagement().stopVPN();
}
catch (Exception ex){
}
}
private void configureAndStartVpn() {
try {
EditText Et_Ovpn = (EditText) findViewById(R.id.et_ovpn);
String retVal = Et_Ovpn.getText().toString();
if (retVal != null && retVal.trim().length()>0) {
byte[] buffer = retVal.getBytes() ;
VpnProfile vp = saveProfile(buffer) ;
if (vp != null) {
startVPN(vp) ;
}
}
else {
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(MainActivity_vpn.this,"Connecting using the last vpn configuration", duration);
toast.show();
startVPN();
}
} catch (Exception e) {
e.printStackTrace() ;
}
}
private VpnProfile saveProfile(byte [] data) {
ConfigParser cp = new ConfigParser();
try {
InputStreamReader isr = new InputStreamReader(new ByteArrayInputStream(data));
cp.parseConfig(isr);
VpnProfile vp = cp.convertProfile();
ProfileManager vpl = ProfileManager.getInstance(this);
vp.mName = Constants.VPN_PROFILE_NAME ;
vpl.addProfile(vp);
vpl.saveProfile(this, vp);
vpl.saveProfileList(this);
return vp ;
} catch(Exception e) {
return null ;
}
}
public void startVPN(VpnProfile vp) {
Intent intent = new Intent(getApplicationContext(),LaunchVPN.class);
intent.putExtra(LaunchVPN.EXTRA_KEY, vp.getUUID().toString());
intent.setAction(Intent.ACTION_MAIN);
startActivity(intent);
}
private void startVPN() {
ProfileManager pm = ProfileManager.getInstance(this) ;
VpnProfile profile = pm.getProfileByName(Constants.VPN_PROFILE_NAME) ;
if (profile == null) {
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(MainActivity_vpn.this,"There are no VPN Configurations.So paste the .OVPN and try", duration);
toast.show();
return ;
}
Intent intent = new Intent(this,LaunchVPN.class);
intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUID().toString());
intent.setAction(Intent.ACTION_MAIN);
startActivity(intent);
}
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
stopVPN();
startVPN();
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(broadcastReceiver);
}
}
答案 0 :(得分:0)
从日志中我看到了这一行:
MainActivity_vpn has leaked ServiceConnection
在您的活动OnCreate
中,您对服务具有约束力,但您永远不会解除绑定。
在onDestroy
:
MainActivity
方法中
if (mServiceConn != null) {
unbindService(mServiceConn);
}
另外,请看: JNI- java.lang.UnsatisfiedLinkError: Native method not found
我认为您应该从方法签名中删除静态
答案 1 :(得分:0)
我没有考虑到本机方法的声明应该放在与捐赠项目中具有相同名称和包的类中。 一旦我更改了本机方法的包名称,问题就解决了
答案 2 :(得分:0)
private VpnProfile saveProfile(byte [] data) {
ConfigParser cp = new ConfigParser();
try {
InputStreamReader isr = new InputStreamReader(new ByteArrayInputStream(data));
cp.parseConfig(isr);
这里ConfigParser()不是公共类,因此您可以创建它的实例。