|
|
|
|
@@ -18,8 +18,7 @@ var spawn = require('win-spawn')
|
|
|
|
|
, rimraf = require('rimraf')
|
|
|
|
|
, Logcat = require('./logcat.js')
|
|
|
|
|
, isWindows = helpers.isWindows()
|
|
|
|
|
, helperJarPath = path.resolve(__dirname, 'helpers')
|
|
|
|
|
, deviceState = require('./device-state.js');
|
|
|
|
|
, helperJarPath = path.resolve(__dirname, 'helpers');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var ADB = function(opts) {
|
|
|
|
|
@@ -53,7 +52,6 @@ ADB.prototype.debug = function(msg) {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ADB.prototype.checkSdkBinaryPresent = function(binary, cb) {
|
|
|
|
|
logger.info("Checking whether " + binary + " is present");
|
|
|
|
|
var binaryLoc = null;
|
|
|
|
|
@@ -79,9 +77,9 @@ ADB.prototype.checkSdkBinaryPresent = function(binary, cb) {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (binaryLoc === null) {
|
|
|
|
|
cb(new Error("Could not find " + binary + " in tools, platform-tools, or build-tools under \"" + this.sdkRoot + "\"; " +
|
|
|
|
|
"do you have android SDK installed into this location?"),
|
|
|
|
|
null);
|
|
|
|
|
cb(new Error("Could not find " + binary + " in tools, platform-tools, " +
|
|
|
|
|
"or build-tools under \"" + this.sdkRoot + "\"; " +
|
|
|
|
|
"do you have android SDK installed into this location?"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.debug("Using " + binary + " from " + binaryLoc);
|
|
|
|
|
@@ -96,8 +94,7 @@ ADB.prototype.checkSdkBinaryPresent = function(binary, cb) {
|
|
|
|
|
} else {
|
|
|
|
|
cb(new Error("Could not find " + binary + "; do you have the Android " +
|
|
|
|
|
"SDK installed and the tools + platform-tools folders " +
|
|
|
|
|
"added to your PATH?"),
|
|
|
|
|
null);
|
|
|
|
|
"added to your PATH?"));
|
|
|
|
|
}
|
|
|
|
|
}.bind(this));
|
|
|
|
|
}
|
|
|
|
|
@@ -160,7 +157,7 @@ ADB.prototype.compileManifest = function(manifest, manifestPackage, targetPackag
|
|
|
|
|
return cb("error compiling manifest");
|
|
|
|
|
}
|
|
|
|
|
logger.debug("Compiled manifest");
|
|
|
|
|
cb(null);
|
|
|
|
|
cb();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -175,7 +172,7 @@ ADB.prototype.insertManifest = function(manifest, srcApk, dstApk, cb) {
|
|
|
|
|
logger.debug(stderr);
|
|
|
|
|
return cb(err);
|
|
|
|
|
}
|
|
|
|
|
cb(null);
|
|
|
|
|
cb();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -195,7 +192,8 @@ ADB.prototype.insertManifest = function(manifest, srcApk, dstApk, cb) {
|
|
|
|
|
java = isWindows ? '"' + java + '.exe"' : '"' + java + '"';
|
|
|
|
|
var moveManifestCmd = '"' + path.resolve(helperJarPath,
|
|
|
|
|
'move_manifest.jar') + '"';
|
|
|
|
|
moveManifestCmd = [java, '-jar', moveManifestCmd, '"' + dstApk + '"', '"' + manifest + '"'].join(' ');
|
|
|
|
|
moveManifestCmd = [java, '-jar', moveManifestCmd, '"' + dstApk + '"', '"'
|
|
|
|
|
+ manifest + '"'].join(' ');
|
|
|
|
|
|
|
|
|
|
logger.debug("Moving manifest with: " + moveManifestCmd);
|
|
|
|
|
exec(moveManifestCmd, { maxBuffer: 524288 }, function(err) {
|
|
|
|
|
@@ -218,7 +216,7 @@ ADB.prototype.insertManifest = function(manifest, srcApk, dstApk, cb) {
|
|
|
|
|
return cb(err);
|
|
|
|
|
}
|
|
|
|
|
logger.debug("Inserted manifest.");
|
|
|
|
|
cb(null);
|
|
|
|
|
cb();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
@@ -231,10 +229,10 @@ ADB.prototype.insertManifest = function(manifest, srcApk, dstApk, cb) {
|
|
|
|
|
], cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// apks is an array of strings.
|
|
|
|
|
ADB.prototype.signDefault = function(apks, cb) {
|
|
|
|
|
ADB.prototype.signWithDefaultCert = function(apks, cb) {
|
|
|
|
|
var signPath = path.resolve(helperJarPath, 'sign.jar');
|
|
|
|
|
var resign = 'java -jar "' + signPath + '" "' + apks.join('" "') + '" --override';
|
|
|
|
|
var resign = 'java -jar "' + signPath + '" "' + apks.join('" "') +
|
|
|
|
|
'" --override';
|
|
|
|
|
logger.debug("Resigning apks with: " + resign);
|
|
|
|
|
exec(resign, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
|
|
|
|
if (stderr.indexOf("Input is not an existing file") !== -1) {
|
|
|
|
|
@@ -247,27 +245,28 @@ ADB.prototype.signDefault = function(apks, cb) {
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// apk is a single apk path
|
|
|
|
|
ADB.prototype.signCustom = function(apk, cb) {
|
|
|
|
|
ADB.prototype.signWithCustomCert = function(apk, cb) {
|
|
|
|
|
var jarsigner = path.resolve(process.env.JAVA_HOME, 'bin', 'jarsigner');
|
|
|
|
|
jarsigner = isWindows ? '"' + jarsigner + '.exe"' : '"' + jarsigner + '"';
|
|
|
|
|
var java = path.resolve(process.env.JAVA_HOME, 'bin', 'java');
|
|
|
|
|
java = isWindows ? '"' + java + '.exe"' : '"' + java + '"';
|
|
|
|
|
var unsign = '"' + path.resolve(helperJarPath, 'unsign.jar') + '"';
|
|
|
|
|
unsign = [java, '-jar', unsign, '"' + apk + '"'].join(' ');
|
|
|
|
|
// "jarsigner" "blank.apk" -sigalg MD5withRSA -digestalg SHA1
|
|
|
|
|
// -keystore "./key.keystore" -storepass "android"
|
|
|
|
|
// -keypass "android" "androiddebugkey"
|
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(this.keystorePath)) {
|
|
|
|
|
return cb(new Error("Keystore doesn't exist. " + this.keystorePath));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sign = [jarsigner, '"' + apk + '"', '-sigalg MD5withRSA', '-digestalg SHA1',
|
|
|
|
|
'-keystore "' + this.keystorePath + '"', '-storepass "' + this.keystorePassword + '"',
|
|
|
|
|
'-keypass "' + this.keyPassword + '"', '"' + this.keyAlias + '"'].join(' ');
|
|
|
|
|
var sign = [jarsigner, '"' + apk + '"',
|
|
|
|
|
'-sigalg MD5withRSA',
|
|
|
|
|
'-digestalg SHA1',
|
|
|
|
|
'-keystore "' + this.keystorePath + '"',
|
|
|
|
|
'-storepass "' + this.keystorePassword + '"',
|
|
|
|
|
'-keypass "' + this.keyPassword + '"',
|
|
|
|
|
'"' + this.keyAlias + '"'].join(' ');
|
|
|
|
|
logger.debug("Unsigning apk with: " + unsign);
|
|
|
|
|
exec(unsign, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
|
|
|
|
if (stderr) {
|
|
|
|
|
if (err || stderr) {
|
|
|
|
|
logger.warn(stderr);
|
|
|
|
|
return cb(new Error("Could not unsign apk. Are you sure " +
|
|
|
|
|
"the file path is correct: " +
|
|
|
|
|
@@ -275,7 +274,7 @@ ADB.prototype.signCustom = function(apk, cb) {
|
|
|
|
|
}
|
|
|
|
|
logger.debug("Signing apk with: " + sign);
|
|
|
|
|
exec(sign, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
|
|
|
|
if (stderr) {
|
|
|
|
|
if (err || stderr) {
|
|
|
|
|
logger.warn(stderr);
|
|
|
|
|
return cb(new Error("Could not sign apk. Are you sure " +
|
|
|
|
|
"the file path is correct: " +
|
|
|
|
|
@@ -286,14 +285,13 @@ ADB.prototype.signCustom = function(apk, cb) {
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// apks is an array of strings.
|
|
|
|
|
ADB.prototype.sign = function(apks, cb) {
|
|
|
|
|
if (this.useKeystore) {
|
|
|
|
|
async.each(apks, this.signCustom.bind(this), function(err) {
|
|
|
|
|
async.each(apks, this.signWithCustomCert.bind(this), function(err) {
|
|
|
|
|
cb(err);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.signDefault(apks, cb);
|
|
|
|
|
this.signWithDefaultCert(apks, cb);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -305,78 +303,7 @@ ADB.prototype.checkApkCert = function(apk, pkg, cb) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.useKeystore) {
|
|
|
|
|
var h = "a-fA-F0-9";
|
|
|
|
|
var md5Str = ['.*MD5.*((?:[', h, ']{2}:){15}[', h, ']{2})'].join('');
|
|
|
|
|
var md5 = new RegExp(md5Str, 'mi');
|
|
|
|
|
var keytool = path.resolve(process.env.JAVA_HOME, 'bin', 'keytool');
|
|
|
|
|
keytool = isWindows ? '"' + keytool + '.exe"' : '"' + keytool + '"';
|
|
|
|
|
var keystoreHash = null;
|
|
|
|
|
|
|
|
|
|
var checkKeystoreMD5 = function(innerCb) {
|
|
|
|
|
logger.debug("checkKeystoreMD5");
|
|
|
|
|
// get keystore md5
|
|
|
|
|
var keystore = [keytool, '-v', '-list', '-alias "' + this.keyAlias + '"',
|
|
|
|
|
'-keystore "' + this.keystorePath + '"', '-storepass "' + this.keystorePassword + '"'].join(' ');
|
|
|
|
|
logger.debug("Printing keystore md5: " + keystore);
|
|
|
|
|
exec(keystore, { maxBuffer: 524288 }, function(err, stdout) {
|
|
|
|
|
keystoreHash = md5.exec(stdout);
|
|
|
|
|
keystoreHash = keystoreHash ? keystoreHash[1] : null;
|
|
|
|
|
logger.debug(' Keystore MD5: ' + keystoreHash);
|
|
|
|
|
innerCb();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var match = false;
|
|
|
|
|
var checkApkMD5 = function(innerCb) {
|
|
|
|
|
logger.debug("checkApkMD5");
|
|
|
|
|
var entryHash = null;
|
|
|
|
|
var zip = new AdmZip(apk);
|
|
|
|
|
var rsa = /^META-INF\/.*\.[rR][sS][aA]$/;
|
|
|
|
|
var entries = zip.getEntries();
|
|
|
|
|
var next = function() {
|
|
|
|
|
var entry = entries.pop(); // meta-inf tends to be at the end
|
|
|
|
|
if (!entry) return innerCb(); // no more entries
|
|
|
|
|
entry = entry.entryName;
|
|
|
|
|
if (!rsa.test(entry)) return next();
|
|
|
|
|
logger.debug("Entry: " + entry);
|
|
|
|
|
var entryPath = path.join(getTempPath(), pkg, 'cert');
|
|
|
|
|
logger.debug("entryPath: " + entryPath);
|
|
|
|
|
var entryFile = path.join(entryPath, entry);
|
|
|
|
|
logger.debug("entryFile: " + entryFile);
|
|
|
|
|
// ensure /tmp/pkg/cert/ doesn't exist or extract will fail.
|
|
|
|
|
rimraf.sync(entryPath);
|
|
|
|
|
// META-INF/CERT.RSA
|
|
|
|
|
zip.extractEntryTo(entry, entryPath, true); // overwrite = true
|
|
|
|
|
logger.debug("extracted!");
|
|
|
|
|
// check for match
|
|
|
|
|
var md5Entry = [keytool, '-v', '-printcert', '-file', entryFile].join(' ');
|
|
|
|
|
logger.debug("Printing apk md5: " + md5Entry);
|
|
|
|
|
exec(md5Entry, { maxBuffer: 524288 }, function(err, stdout) {
|
|
|
|
|
entryHash = md5.exec(stdout);
|
|
|
|
|
entryHash = entryHash ? entryHash[1] : null;
|
|
|
|
|
logger.debug('entryHash MD5: ' + entryHash);
|
|
|
|
|
logger.debug(' keystore MD5: ' + keystoreHash);
|
|
|
|
|
var matchesKeystore = entryHash && entryHash === keystoreHash;
|
|
|
|
|
logger.debug('Matches keystore? ' + matchesKeystore);
|
|
|
|
|
if (matchesKeystore) {
|
|
|
|
|
match = true;
|
|
|
|
|
return innerCb();
|
|
|
|
|
} else {
|
|
|
|
|
next();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}.bind(this);
|
|
|
|
|
next();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async.series([
|
|
|
|
|
function(cb) { checkKeystoreMD5(cb); },
|
|
|
|
|
function(cb) { checkApkMD5(cb); }
|
|
|
|
|
], function() { logger.debug("checkApkCert match? " + match);
|
|
|
|
|
cb(null, match); });
|
|
|
|
|
|
|
|
|
|
// exit checkApkCert
|
|
|
|
|
return;
|
|
|
|
|
return this.checkCustomApkCert(apk, pkg, cb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var verifyPath = path.resolve(helperJarPath, 'verify.jar');
|
|
|
|
|
@@ -392,6 +319,78 @@ ADB.prototype.checkApkCert = function(apk, pkg, cb) {
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.checkCustomApkCert = function(apk, pkg, cb) {
|
|
|
|
|
var h = "a-fA-F0-9";
|
|
|
|
|
var md5Str = ['.*MD5.*((?:[', h, ']{2}:){15}[', h, ']{2})'].join('');
|
|
|
|
|
var md5 = new RegExp(md5Str, 'mi');
|
|
|
|
|
var keytool = path.resolve(process.env.JAVA_HOME, 'bin', 'keytool');
|
|
|
|
|
keytool = isWindows ? '"' + keytool + '.exe"' : '"' + keytool + '"';
|
|
|
|
|
|
|
|
|
|
this.getKeystoreMd5(keytool, md5, function(err, keystoreHash) {
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
this.checkApkKeystoreMatch(keytool, md5, keystoreHash, apk, pkg, cb);
|
|
|
|
|
}.bind(this));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.getKeystoreMd5 = function(keytool, md5re, cb) {
|
|
|
|
|
var keystoreHash;
|
|
|
|
|
var keystore = [keytool, '-v', '-list',
|
|
|
|
|
'-alias "' + this.keyAlias + '"',
|
|
|
|
|
'-keystore "' + this.keystorePath + '"',
|
|
|
|
|
'-storepass "' + this.keystorePassword + '"'].join(' ');
|
|
|
|
|
logger.debug("Printing keystore md5: " + keystore);
|
|
|
|
|
exec(keystore, { maxBuffer: 524288 }, function(err, stdout) {
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
keystoreHash = md5re.exec(stdout);
|
|
|
|
|
keystoreHash = keystoreHash ? keystoreHash[1] : null;
|
|
|
|
|
logger.debug('Keystore MD5: ' + keystoreHash);
|
|
|
|
|
cb(null, keystoreHash);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.checkApkKeystoreMatch = function(keytool, md5re, keystoreHash,
|
|
|
|
|
pkg, apk, cb) {
|
|
|
|
|
var entryHash = null;
|
|
|
|
|
var zip = new AdmZip(apk);
|
|
|
|
|
var rsa = /^META-INF\/.*\.[rR][sS][aA]$/;
|
|
|
|
|
var entries = zip.getEntries();
|
|
|
|
|
|
|
|
|
|
var next = function() {
|
|
|
|
|
var entry = entries.pop(); // meta-inf tends to be at the end
|
|
|
|
|
if (!entry) return cb(null, false); // no more entries
|
|
|
|
|
entry = entry.entryName;
|
|
|
|
|
if (!rsa.test(entry)) return next();
|
|
|
|
|
logger.debug("Entry: " + entry);
|
|
|
|
|
var entryPath = path.join(getTempPath(), pkg, 'cert');
|
|
|
|
|
logger.debug("entryPath: " + entryPath);
|
|
|
|
|
var entryFile = path.join(entryPath, entry);
|
|
|
|
|
logger.debug("entryFile: " + entryFile);
|
|
|
|
|
// ensure /tmp/pkg/cert/ doesn't exist or extract will fail.
|
|
|
|
|
rimraf.sync(entryPath);
|
|
|
|
|
// META-INF/CERT.RSA
|
|
|
|
|
zip.extractEntryTo(entry, entryPath, true); // overwrite = true
|
|
|
|
|
logger.debug("extracted!");
|
|
|
|
|
// check for match
|
|
|
|
|
var md5Entry = [keytool, '-v', '-printcert', '-file', entryFile].join(' ');
|
|
|
|
|
logger.debug("Printing apk md5: " + md5Entry);
|
|
|
|
|
exec(md5Entry, { maxBuffer: 524288 }, function(err, stdout) {
|
|
|
|
|
entryHash = md5re.exec(stdout);
|
|
|
|
|
entryHash = entryHash ? entryHash[1] : null;
|
|
|
|
|
logger.debug('entryHash MD5: ' + entryHash);
|
|
|
|
|
logger.debug(' keystore MD5: ' + keystoreHash);
|
|
|
|
|
var matchesKeystore = entryHash && entryHash === keystoreHash;
|
|
|
|
|
logger.debug('Matches keystore? ' + matchesKeystore);
|
|
|
|
|
if (matchesKeystore) {
|
|
|
|
|
return cb(null, true);
|
|
|
|
|
} else {
|
|
|
|
|
next();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
|
|
next();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.getDevicesWithRetry = function(timeoutMs, cb) {
|
|
|
|
|
if (typeof timeoutMs === "function") {
|
|
|
|
|
cb = timeoutMs;
|
|
|
|
|
@@ -472,7 +471,8 @@ ADB.prototype.killAllEmulators = function(cb) {
|
|
|
|
|
"/usr/bin/killall -m emulator*";
|
|
|
|
|
exec(killallCmd, { maxBuffer: 524288 }, function(err) {
|
|
|
|
|
if (err) {
|
|
|
|
|
logger.info("Could not kill emulator. It was probably not running. : " + err.message);
|
|
|
|
|
logger.info("Could not kill emulator. It was probably not running.: " +
|
|
|
|
|
err.message);
|
|
|
|
|
}
|
|
|
|
|
cb();
|
|
|
|
|
});
|
|
|
|
|
@@ -487,8 +487,12 @@ ADB.prototype.launchAVD = function(avdName, cb) {
|
|
|
|
|
avdName = "@" + avdName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spawn(emulatorBinaryPath.substr(1, emulatorBinaryPath.length - 2),
|
|
|
|
|
[avdName]);
|
|
|
|
|
try {
|
|
|
|
|
spawn(emulatorBinaryPath.substr(1, emulatorBinaryPath.length - 2),
|
|
|
|
|
[avdName]);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
return cb(err);
|
|
|
|
|
}
|
|
|
|
|
this.getDeviceWithRetry(120000, cb);
|
|
|
|
|
}.bind(this));
|
|
|
|
|
};
|
|
|
|
|
@@ -594,9 +598,7 @@ ADB.prototype.waitForDevice = function(cb) {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.restartAdb = function(cb) {
|
|
|
|
|
logger.info("Killing ADB server so it will come back online");
|
|
|
|
|
var cmd = this.adb + " kill-server";
|
|
|
|
|
exec(cmd, { maxBuffer: 524288 }, function(err) {
|
|
|
|
|
this.exec("kill-server", function(err) {
|
|
|
|
|
if (err) {
|
|
|
|
|
logger.error("Error killing ADB server, going to see if it's online " +
|
|
|
|
|
"anyway");
|
|
|
|
|
@@ -684,7 +686,7 @@ ADB.prototype.waitForActivityOrNot = function(pkg, activity, not,
|
|
|
|
|
waitMs = 20000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.info("Waiting for app's activity to not be focused");
|
|
|
|
|
logger.info("Waiting for activity to " + (not ? "not" : "") + " be focused");
|
|
|
|
|
var intMs = 750
|
|
|
|
|
, endAt = Date.now() + waitMs;
|
|
|
|
|
|
|
|
|
|
@@ -843,7 +845,6 @@ ADB.prototype.isAppInstalled = function(pkg, cb) {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.back = function(cb) {
|
|
|
|
|
this.requireDeviceId();
|
|
|
|
|
this.debug("Pressing the BACK button");
|
|
|
|
|
var cmd = this.adbCmd + " shell input keyevent 4";
|
|
|
|
|
exec(cmd, { maxBuffer: 524288 }, function() {
|
|
|
|
|
@@ -852,67 +853,45 @@ ADB.prototype.back = function(cb) {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.goToHome = function(cb) {
|
|
|
|
|
this.requireDeviceId();
|
|
|
|
|
this.debug("Pressing the HOME button");
|
|
|
|
|
var cmd = this.adbCmd + " shell input keyevent 3";
|
|
|
|
|
exec(cmd, { maxBuffer: 524288 }, function() {
|
|
|
|
|
cb();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.wakeUp = function(cb) {
|
|
|
|
|
// requires an appium bootstrap connection loaded
|
|
|
|
|
this.debug("Waking up device if it's not alive");
|
|
|
|
|
this.android.proxy(["wake", {}], cb);
|
|
|
|
|
this.keyevent(3, cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.keyevent = function(keycode, cb) {
|
|
|
|
|
this.requireDeviceId();
|
|
|
|
|
var code = parseInt(keycode, 10);
|
|
|
|
|
// keycode must be an int.
|
|
|
|
|
var cmd = this.adbCmd + ' shell input keyevent ' + code;
|
|
|
|
|
this.debug("Sending keyevent " + code);
|
|
|
|
|
exec(cmd, { maxBuffer: 524288 }, function() {
|
|
|
|
|
cb();
|
|
|
|
|
});
|
|
|
|
|
var cmd = 'input keyevent ' + code;
|
|
|
|
|
this.shell(cmd, cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.unlockScreen = function(cb) {
|
|
|
|
|
this.requireDeviceId();
|
|
|
|
|
deviceState.isScreenLocked(this.adbCmd, function(err, isLocked) {
|
|
|
|
|
if (err) {
|
|
|
|
|
cb(err);
|
|
|
|
|
} else {
|
|
|
|
|
if (isLocked) {
|
|
|
|
|
this.debug("Attempting to unlock screen by starting Unlock app...");
|
|
|
|
|
var cmd = this.adbCmd + " shell am start -n io.appium.unlock/.Unlock";
|
|
|
|
|
exec(cmd, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
|
|
|
|
if (err) {
|
|
|
|
|
cb(err);
|
|
|
|
|
} else {
|
|
|
|
|
var intervalRepeat = 200;
|
|
|
|
|
var interval = function() {
|
|
|
|
|
deviceState.isScreenLocked(this.adbCmd, function(err, isLocked) {
|
|
|
|
|
if (err) {
|
|
|
|
|
cb(err);
|
|
|
|
|
} else {
|
|
|
|
|
if (isLocked) {
|
|
|
|
|
setTimeout(interval, intervalRepeat);
|
|
|
|
|
} else {
|
|
|
|
|
cb();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}.bind(this);
|
|
|
|
|
setTimeout(interval, intervalRepeat);
|
|
|
|
|
}
|
|
|
|
|
}.bind(this));
|
|
|
|
|
ADB.prototype.isScreenLocked = function(cb) {
|
|
|
|
|
var cmd = "dumpsys window";
|
|
|
|
|
this.exec(cmd, function(err, stdout) {
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
|
|
var screenLocked = /mShowingLockscreen=\w+/gi.exec(stdout);
|
|
|
|
|
var samsungNoteUnlocked = /mScreenOnFully=\w+/gi.exec(stdout);
|
|
|
|
|
var gbScreenLocked = /mCurrentFocus.+Keyguard/gi.exec(stdout);
|
|
|
|
|
|
|
|
|
|
if (screenLocked && screenLocked[0]) {
|
|
|
|
|
if (screenLocked[0].split('=')[1] == 'false') {
|
|
|
|
|
cb(null, false);
|
|
|
|
|
} else {
|
|
|
|
|
this.debug('Screen already unlocked, continuing.');
|
|
|
|
|
cb();
|
|
|
|
|
cb(null, true);
|
|
|
|
|
}
|
|
|
|
|
} else if (samsungNoteUnlocked && samsungNoteUnlocked[0]) {
|
|
|
|
|
if (samsungNoteUnlocked[0].split('=')[1] == 'true') {
|
|
|
|
|
cb(null, false);
|
|
|
|
|
} else {
|
|
|
|
|
cb(null, true);
|
|
|
|
|
}
|
|
|
|
|
} else if (gbScreenLocked && gbScreenLocked[0]) {
|
|
|
|
|
cb(null, true);
|
|
|
|
|
} else {
|
|
|
|
|
cb(null, false);
|
|
|
|
|
}
|
|
|
|
|
}.bind(this));
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ADB.prototype.sendTelnetCommand = function(command, cb) {
|
|
|
|
|
@@ -957,5 +936,4 @@ ADB.prototype.sendTelnetCommand = function(command, cb) {
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = ADB;
|
|
|
|
|
|