mirror of https://github.com/m2049r/xmrwallet.git
new Ledger interface
This commit is contained in:
parent
f01a8eac5d
commit
3e90f2e22e
|
@ -37,7 +37,8 @@ android {
|
||||||
abi {
|
abi {
|
||||||
enable true
|
enable true
|
||||||
reset()
|
reset()
|
||||||
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
//include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||||
|
include 'arm64-v8a'
|
||||||
universalApk true
|
universalApk true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright (c) 2017-2018, The Monero Project
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if defined(HAVE_MONERUJO)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LedgerFind - find Ledger Device and return it's name
|
||||||
|
* @param buffer - buffer for name of found device
|
||||||
|
* @param len - length of buffer
|
||||||
|
* @return 0 - success
|
||||||
|
* -1 - no device connected / found
|
||||||
|
* -2 - JVM not found
|
||||||
|
*/
|
||||||
|
int LedgerFind(char *buffer, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LedgerExchange - exchange data with Ledger Device
|
||||||
|
* @param command - buffer for data to send
|
||||||
|
* @param cmd_len - length of send to send
|
||||||
|
* @param response - buffer for received data
|
||||||
|
* @param max_resp_len - size of receive buffer
|
||||||
|
*
|
||||||
|
* @return length of received data in response or -1 if error
|
||||||
|
*/
|
||||||
|
int LedgerExchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "device_io.hpp"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace hw {
|
||||||
|
namespace io {
|
||||||
|
class device_io_monerujo: device_io {
|
||||||
|
public:
|
||||||
|
device_io_monerujo() {};
|
||||||
|
~device_io_monerujo() {};
|
||||||
|
|
||||||
|
void init() {};
|
||||||
|
void release() {};
|
||||||
|
|
||||||
|
void connect(void *params) {};
|
||||||
|
void disconnect() {};
|
||||||
|
bool connected() const {return true;}; // monerujo is always connected before it gets here
|
||||||
|
|
||||||
|
// returns number of bytes read or -1 on error
|
||||||
|
int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len) {
|
||||||
|
return LedgerExchange(command, cmd_len, response, max_resp_len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //#if defined(HAVE_MONERUJO)
|
|
@ -1386,24 +1386,22 @@ Java_com_m2049r_xmrwallet_model_WalletManager_setLogLevel(JNIEnv *env, jobject i
|
||||||
// Ledger Stuff
|
// Ledger Stuff
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "monerujo_ledger.h"
|
#include "device_io_monerujo.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief LedgerExchange - exchange data with Ledger Device
|
* @brief LedgerExchange - exchange data with Ledger Device
|
||||||
* @param pbSendBuffer - buffer for data to send
|
* @param command - buffer for data to send
|
||||||
* @param cbSendLength - length of send buffer
|
* @param cmd_len - length of send to send
|
||||||
* @param pbRecvBuffer - buffer for received data
|
* @param response - buffer for received data
|
||||||
* @param pcbRecvLength - pointer to size of receive buffer
|
* @param max_resp_len - size of receive buffer
|
||||||
* gets set with length of received data on successful return
|
*
|
||||||
* @return SCARD_S_SUCCESS - success
|
* @return length of received data in response or -1 if error
|
||||||
* SCARD_E_NO_READERS_AVAILABLE - no device connected / found
|
|
||||||
* SCARD_E_INSUFFICIENT_BUFFER - pbRecvBuffer is too small for the response
|
|
||||||
*/
|
*/
|
||||||
LONG LedgerExchange(
|
int LedgerExchange(
|
||||||
LPCBYTE pbSendBuffer,
|
unsigned char *command,
|
||||||
DWORD cbSendLength,
|
unsigned int cmd_len,
|
||||||
LPBYTE pbRecvBuffer,
|
unsigned char *response,
|
||||||
LPDWORD pcbRecvLength) {
|
unsigned int max_resp_len) {
|
||||||
LOGD("LedgerExchange");
|
LOGD("LedgerExchange");
|
||||||
JNIEnv *jenv;
|
JNIEnv *jenv;
|
||||||
int envStat = attachJVM(&jenv);
|
int envStat = attachJVM(&jenv);
|
||||||
|
@ -1411,30 +1409,29 @@ LONG LedgerExchange(
|
||||||
|
|
||||||
jmethodID exchangeMethod = jenv->GetStaticMethodID(class_Ledger, "Exchange", "([B)[B");
|
jmethodID exchangeMethod = jenv->GetStaticMethodID(class_Ledger, "Exchange", "([B)[B");
|
||||||
|
|
||||||
jsize sendLen = static_cast<jsize>(cbSendLength);
|
jsize sendLen = static_cast<jsize>(cmd_len);
|
||||||
jbyteArray dataSend = jenv->NewByteArray(sendLen);
|
jbyteArray dataSend = jenv->NewByteArray(sendLen);
|
||||||
jenv->SetByteArrayRegion(dataSend, 0, sendLen, (jbyte *) pbSendBuffer);
|
jenv->SetByteArrayRegion(dataSend, 0, sendLen, (jbyte *) command);
|
||||||
jbyteArray dataRecv = (jbyteArray) jenv->CallStaticObjectMethod(class_Ledger, exchangeMethod,
|
jbyteArray dataRecv = (jbyteArray) jenv->CallStaticObjectMethod(class_Ledger, exchangeMethod,
|
||||||
dataSend);
|
dataSend);
|
||||||
jenv->DeleteLocalRef(dataSend);
|
jenv->DeleteLocalRef(dataSend);
|
||||||
if (dataRecv == nullptr) {
|
if (dataRecv == nullptr) {
|
||||||
detachJVM(jenv, envStat);
|
detachJVM(jenv, envStat);
|
||||||
LOGD("LedgerExchange SCARD_E_NO_READERS_AVAILABLE");
|
LOGD("LedgerExchange SCARD_E_NO_READERS_AVAILABLE");
|
||||||
return SCARD_E_NO_READERS_AVAILABLE;
|
return -1;
|
||||||
}
|
}
|
||||||
jsize len = jenv->GetArrayLength(dataRecv);
|
jsize len = jenv->GetArrayLength(dataRecv);
|
||||||
LOGD("LedgerExchange SCARD_S_SUCCESS %ld/%d", cbSendLength, len);
|
LOGD("LedgerExchange SCARD_S_SUCCESS %ld/%d", cmd_len, len);
|
||||||
if (len <= *pcbRecvLength) {
|
if (len <= max_resp_len) {
|
||||||
*pcbRecvLength = static_cast<DWORD>(len);
|
jenv->GetByteArrayRegion(dataRecv, 0, len, (jbyte *) response);
|
||||||
jenv->GetByteArrayRegion(dataRecv, 0, len, (jbyte *) pbRecvBuffer);
|
|
||||||
jenv->DeleteLocalRef(dataRecv);
|
jenv->DeleteLocalRef(dataRecv);
|
||||||
detachJVM(jenv, envStat);
|
detachJVM(jenv, envStat);
|
||||||
return SCARD_S_SUCCESS;
|
return static_cast<int>(len);;
|
||||||
} else {
|
} else {
|
||||||
jenv->DeleteLocalRef(dataRecv);
|
jenv->DeleteLocalRef(dataRecv);
|
||||||
detachJVM(jenv, envStat);
|
detachJVM(jenv, envStat);
|
||||||
LOGE("LedgerExchange SCARD_E_INSUFFICIENT_BUFFER");
|
LOGE("LedgerExchange SCARD_E_INSUFFICIENT_BUFFER");
|
||||||
return SCARD_E_INSUFFICIENT_BUFFER;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2018 m2049r
|
|
||||||
* <p>
|
|
||||||
* 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
|
|
||||||
* <p>
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* <p>
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef XMRWALLET_LEDGER_H
|
|
||||||
#define XMRWALLET_LEDGER_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SCARD_S_SUCCESS ((LONG)0x00000000) /**< No error was encountered. */
|
|
||||||
#define SCARD_E_INSUFFICIENT_BUFFER ((LONG)0x80100008) /**< The data buffer to receive returned data is too small for the returned data. */
|
|
||||||
#define SCARD_E_NO_READERS_AVAILABLE ((LONG)0x8010002E) /**< Cannot find a smart card reader. */
|
|
||||||
|
|
||||||
typedef long LONG;
|
|
||||||
typedef unsigned long DWORD;
|
|
||||||
typedef DWORD *LPDWORD;
|
|
||||||
typedef unsigned char BYTE;
|
|
||||||
typedef BYTE *LPBYTE;
|
|
||||||
typedef const BYTE *LPCBYTE;
|
|
||||||
|
|
||||||
typedef char CHAR;
|
|
||||||
typedef CHAR *LPSTR;
|
|
||||||
|
|
||||||
int LedgerFind(char *buffer, size_t len);
|
|
||||||
LONG LedgerExchange(LPCBYTE pbSendBuffer, DWORD cbSendLength, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif //XMRWALLET_LEDGER_H
|
|
|
@ -1177,6 +1177,8 @@ public class LoginActivity extends BaseActivity
|
||||||
case Device_Ledger:
|
case Device_Ledger:
|
||||||
if (!hasLedger()) {
|
if (!hasLedger()) {
|
||||||
toast(R.string.open_wallet_ledger_missing);
|
toast(R.string.open_wallet_ledger_missing);
|
||||||
|
} else {
|
||||||
|
startWallet(walletName, password, fingerprintUsed);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -529,16 +529,23 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWalletStarted(final boolean success) {
|
public void onWalletStarted(final Wallet.ConnectionStatus connStatus) {
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
dismissProgressDialog();
|
dismissProgressDialog();
|
||||||
if (!success) {
|
switch (connStatus) {
|
||||||
|
case ConnectionStatus_Disconnected:
|
||||||
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show();
|
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
|
case ConnectionStatus_WrongVersion:
|
||||||
|
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_wrongversion), Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
|
case ConnectionStatus_Connected:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!success) {
|
if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
haveWallet = true;
|
haveWallet = true;
|
||||||
|
|
|
@ -34,11 +34,9 @@ import java.io.IOException;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class Ledger {
|
public class Ledger {
|
||||||
// lookahead parameters as suggest on
|
// 5:20 is same as wallet2.cpp::restore()
|
||||||
// https://monero.stackexchange.com/a/9902/8977 (Step 8)
|
static public final int LOOKAHEAD_ACCOUNTS = 5;
|
||||||
// by dEBRUYNE
|
static public final int LOOKAHEAD_SUBADDRESSES = 20;
|
||||||
static public final int LOOKAHEAD_ACCOUNTS = 3;
|
|
||||||
static public final int LOOKAHEAD_SUBADDRESSES = 100;
|
|
||||||
static public final String SUBADDRESS_LOOKAHEAD = LOOKAHEAD_ACCOUNTS + ":" + LOOKAHEAD_SUBADDRESSES;
|
static public final String SUBADDRESS_LOOKAHEAD = LOOKAHEAD_ACCOUNTS + ":" + LOOKAHEAD_SUBADDRESSES;
|
||||||
|
|
||||||
public static final int SW_OK = 0x9000;
|
public static final int SW_OK = 0x9000;
|
||||||
|
|
|
@ -226,7 +226,7 @@ public class WalletService extends Service {
|
||||||
|
|
||||||
void onSetNotes(boolean success);
|
void onSetNotes(boolean success);
|
||||||
|
|
||||||
void onWalletStarted(boolean success);
|
void onWalletStarted(Wallet.ConnectionStatus walletStatus);
|
||||||
|
|
||||||
void onWalletOpen(Wallet.Device device);
|
void onWalletOpen(Wallet.Device device);
|
||||||
}
|
}
|
||||||
|
@ -293,9 +293,9 @@ public class WalletService extends Service {
|
||||||
if (walletId != null) {
|
if (walletId != null) {
|
||||||
showProgress(getString(R.string.status_wallet_loading));
|
showProgress(getString(R.string.status_wallet_loading));
|
||||||
showProgress(10);
|
showProgress(10);
|
||||||
boolean success = start(walletId, walletPw);
|
Wallet.ConnectionStatus connStatus = start(walletId, walletPw);
|
||||||
if (observer != null) observer.onWalletStarted(success);
|
if (observer != null) observer.onWalletStarted(connStatus);
|
||||||
if (!success) {
|
if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
||||||
errorState = true;
|
errorState = true;
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
@ -490,7 +490,7 @@ public class WalletService extends Service {
|
||||||
return true; // true is important so that onUnbind is also called next time
|
return true; // true is important so that onUnbind is also called next time
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean start(String walletName, String walletPassword) {
|
private Wallet.ConnectionStatus start(String walletName, String walletPassword) {
|
||||||
Timber.d("start()");
|
Timber.d("start()");
|
||||||
startNotfication();
|
startNotfication();
|
||||||
showProgress(getString(R.string.status_wallet_loading));
|
showProgress(getString(R.string.status_wallet_loading));
|
||||||
|
@ -498,9 +498,11 @@ public class WalletService extends Service {
|
||||||
if (listener == null) {
|
if (listener == null) {
|
||||||
Timber.d("start() loadWallet");
|
Timber.d("start() loadWallet");
|
||||||
Wallet aWallet = loadWallet(walletName, walletPassword);
|
Wallet aWallet = loadWallet(walletName, walletPassword);
|
||||||
if ((aWallet == null) || (aWallet.getConnectionStatus() != Wallet.ConnectionStatus.ConnectionStatus_Connected)) {
|
Wallet.ConnectionStatus connStatus = Wallet.ConnectionStatus.ConnectionStatus_Disconnected;
|
||||||
|
if (aWallet != null) connStatus = aWallet.getConnectionStatus();
|
||||||
|
if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
||||||
if (aWallet != null) aWallet.close();
|
if (aWallet != null) aWallet.close();
|
||||||
return false;
|
return connStatus;
|
||||||
}
|
}
|
||||||
listener = new MyWalletListener();
|
listener = new MyWalletListener();
|
||||||
listener.start();
|
listener.start();
|
||||||
|
@ -511,7 +513,7 @@ public class WalletService extends Service {
|
||||||
// if we try to refresh the history here we get occasional segfaults!
|
// if we try to refresh the history here we get occasional segfaults!
|
||||||
// doesnt matter since we update as soon as we get a new block anyway
|
// doesnt matter since we update as soon as we get a new block anyway
|
||||||
Timber.d("start() done");
|
Timber.d("start() done");
|
||||||
return true;
|
return Wallet.ConnectionStatus.ConnectionStatus_Connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
@ -551,6 +553,7 @@ public class WalletService extends Service {
|
||||||
if (walletMgr.walletExists(path)) {
|
if (walletMgr.walletExists(path)) {
|
||||||
Timber.d("open wallet %s", path);
|
Timber.d("open wallet %s", path);
|
||||||
Wallet.Device device = WalletManager.getInstance().queryWalletDevice(path + ".keys", walletPassword);
|
Wallet.Device device = WalletManager.getInstance().queryWalletDevice(path + ".keys", walletPassword);
|
||||||
|
Timber.d("device is %s", device.toString());
|
||||||
if (observer != null) observer.onWalletOpen(device);
|
if (observer != null) observer.onWalletOpen(device);
|
||||||
wallet = walletMgr.openWallet(path, walletPassword);
|
wallet = walletMgr.openWallet(path, walletPassword);
|
||||||
showProgress(60);
|
showProgress(60);
|
||||||
|
|
|
@ -119,6 +119,7 @@
|
||||||
<string name="status_wallet_unload_failed">Wallet save failed!</string>
|
<string name="status_wallet_unload_failed">Wallet save failed!</string>
|
||||||
<string name="status_wallet_connecting">Connecting …</string>
|
<string name="status_wallet_connecting">Connecting …</string>
|
||||||
<string name="status_wallet_connect_failed">Node connection failed!\nCheck username/password</string>
|
<string name="status_wallet_connect_failed">Node connection failed!\nCheck username/password</string>
|
||||||
|
<string name="status_wallet_connect_wrongversion">Node version incompatible!</string>
|
||||||
<string name="status_wallet_connect_timeout">Node connection timed out!\nTry again or another.</string>
|
<string name="status_wallet_connect_timeout">Node connection timed out!\nTry again or another.</string>
|
||||||
<string name="status_wallet_node_invalid">Node invalid!\nTry another.</string>
|
<string name="status_wallet_node_invalid">Node invalid!\nTry another.</string>
|
||||||
<string name="status_wallet_connect_ioex">Cannot reach node!\nTry again or another.</string>
|
<string name="status_wallet_connect_ioex">Cannot reach node!\nTry again or another.</string>
|
||||||
|
|
|
@ -7,7 +7,7 @@ set -e
|
||||||
orig_path=$PATH
|
orig_path=$PATH
|
||||||
|
|
||||||
packages=(boost openssl monero)
|
packages=(boost openssl monero)
|
||||||
archs=(arm arm64 x86 x86_64)
|
archs=(arm64 x86_64)
|
||||||
|
|
||||||
for arch in ${archs[@]}; do
|
for arch in ${archs[@]}; do
|
||||||
case ${arch} in
|
case ${arch} in
|
||||||
|
@ -32,11 +32,11 @@ for arch in ${archs[@]}; do
|
||||||
OUTPUT_DIR=`pwd`/$package/lib/$xarch
|
OUTPUT_DIR=`pwd`/$package/lib/$xarch
|
||||||
mkdir -p $OUTPUT_DIR
|
mkdir -p $OUTPUT_DIR
|
||||||
rm -f $OUTPUT_DIR/*.a
|
rm -f $OUTPUT_DIR/*.a
|
||||||
cp -a /opt/android/build/$package/$arch/lib/*.a $OUTPUT_DIR
|
cp -a /media/m2049r/DATA/android/external-libs/build/$package/$arch/lib/*.a $OUTPUT_DIR
|
||||||
|
|
||||||
if [ $package = "monero" -a -d "/opt/android/build/$package/include" ]; then
|
if [ $package = "monero" -a -d "/opt/android/build/$package/include" ]; then
|
||||||
rm -rf $OUTPUT_DIR/../../include
|
rm -rf $OUTPUT_DIR/../../include
|
||||||
cp -a /opt/android/build/$package/include $OUTPUT_DIR/../..
|
cp -a /media/m2049r/DATA/android/external-libs/build/$package/include $OUTPUT_DIR/../..
|
||||||
fi
|
fi
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|
Loading…
Reference in New Issue