修复 打包后ui脚本无法运行的问题

修复 打包后模块无法运行的问题
This commit is contained in:
hyb1996
2018-12-12 14:18:03 +08:00
parent c460425656
commit 05813499bc
33 changed files with 548 additions and 91 deletions

View File

@@ -31,7 +31,7 @@
android:name=".App"
android:allowBackup="false"
android:icon="@drawable/autojs_material"
android:label="@string/_app_name"
android:label="@string/app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="@style/AppTheme"
@@ -45,7 +45,7 @@
<activity
android:name=".ui.splash.SplashActivity"
android:hardwareAccelerated="true"
android:label="@string/_app_name"
android:label="@string/app_name"
android:theme="@style/AppTheme.Splash">
<intent-filter>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -56,7 +56,7 @@
<activity
android:name=".ui.main.MainActivity_"
android:hardwareAccelerated="true"
android:label="@string/_app_name"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/AppTheme.FullScreen">
</activity>
@@ -164,7 +164,7 @@
android:name=".external.tasker.PluginActivity"
android:exported="true"
android:icon="@drawable/ic_android_eat_js"
android:label="@string/_app_name"
android:label="@string/app_name"
android:targetActivity=".external.tasker.TaskPrefEditActivity_"
tools:ignore="ExportedActivity">
<intent-filter>
@@ -277,7 +277,7 @@
</activity>
<service
android:name="com.stardust.notification.NotificationListenerService"
android:label="@string/_app_name"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService"/>

View File

@@ -9,6 +9,8 @@ import com.stardust.autojs.apkbuilder.ManifestEditor;
import com.stardust.autojs.apkbuilder.util.StreamUtils;
import com.stardust.autojs.project.BuildInfo;
import com.stardust.autojs.project.ProjectConfig;
import com.stardust.autojs.script.EncryptedScriptFileHeader;
import com.stardust.autojs.script.JavaScriptFileSource;
import com.stardust.pio.PFiles;
import com.stardust.util.AdvancedEncryptionStandard;
import com.stardust.util.MD5;
@@ -198,12 +200,16 @@ public class ApkBuilder {
}
private void encrypt(File toDir, File file) throws IOException {
PFiles.writeBytes(new File(toDir, file.getName()).getPath(), encrypt(file));
FileOutputStream fos = new FileOutputStream(new File(toDir, file.getName()));
EncryptedScriptFileHeader.INSTANCE.writeHeader(fos, (short) new JavaScriptFileSource(file).getExecutionMode());
encrypt(fos, file);
}
private byte[] encrypt(File file) throws IOException {
private void encrypt(FileOutputStream fos, File file) throws IOException {
try {
return new AdvancedEncryptionStandard(mKey.getBytes(), mInitVector).encrypt(PFiles.readBytes(file.getPath()));
byte[] bytes = new AdvancedEncryptionStandard(mKey.getBytes(), mInitVector).encrypt(PFiles.readBytes(file.getPath()));
fos.write(bytes);
fos.close();
} catch (Exception e) {
throw new IOException(e);
}
@@ -212,7 +218,7 @@ public class ApkBuilder {
public ApkBuilder replaceFile(String relativePath, String newFilePath) throws IOException {
if (newFilePath.endsWith(".js")) {
PFiles.writeBytes(new File(mWorkspacePath, relativePath).getPath(), encrypt(new File(newFilePath)));
encrypt(new FileOutputStream(new File(mWorkspacePath, relativePath)), new File(newFilePath));
} else {
StreamUtils.write(new FileInputStream(newFilePath), new FileOutputStream(new File(mWorkspacePath, relativePath)));
}

View File

@@ -5,6 +5,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import com.stardust.pio.UncheckedIOException;
import org.autojs.autojs.BuildConfig;
import com.stardust.util.DeveloperUtils;
@@ -20,6 +21,7 @@ public class ApkBuilderPluginHelper {
private static final String PLUGIN_PACKAGE_NAME = "org.autojs.apkbuilderplugin";
private static final String TEMPLATE_APK_PATH = "template.apk";
private static final boolean DEBUG_APK_PLUGIN = false;
public static boolean isPluginAvailable(Context context) {
return DeveloperUtils.checkSignature(context, PLUGIN_PACKAGE_NAME);
@@ -27,9 +29,9 @@ public class ApkBuilderPluginHelper {
public static InputStream openTemplateApk(Context context) {
try {
//if (BuildConfig.DEBUG) {
// return context.getAssets().open(TEMPLATE_APK_PATH);
//}
if (DEBUG_APK_PLUGIN && BuildConfig.DEBUG) {
return context.getAssets().open(TEMPLATE_APK_PATH);
}
return context.getPackageManager().getResourcesForApplication(PLUGIN_PACKAGE_NAME)
.getAssets().open(TEMPLATE_APK_PATH);
} catch (IOException e) {

View File

@@ -28,7 +28,6 @@ public class ScriptIntents {
public static final String EXTRA_KEY_LOOP_INTERVAL = "interval";
public static final String EXTRA_KEY_DELAY = "delay";
public static boolean isTaskerBundleValid(Bundle bundle) {
return bundle.containsKey(ScriptIntents.EXTRA_KEY_PATH) || bundle.containsKey(EXTRA_KEY_PRE_EXECUTE_SCRIPT);
}

View File

@@ -6,7 +6,6 @@ import android.text.TextUtils;
import com.stardust.app.GlobalAppContext;
import org.autojs.autojs.Pref;
import org.autojs.autojs.App;
import org.autojs.autojs.R;
import com.stardust.autojs.core.accessibility.AccessibilityService;
@@ -36,12 +35,12 @@ public class AccessibilityServiceTool {
public static void goToAccessibilitySetting() {
Context context = GlobalAppContext.get();
if (Pref.isFirstGoToAccessibilitySetting()) {
GlobalAppContext.toast(context.getString(R.string.text_please_choose) + context.getString(R.string._app_name));
GlobalAppContext.toast(context.getString(R.string.text_please_choose) + context.getString(R.string.app_name));
}
try {
AccessibilityServiceUtils.INSTANCE.goToAccessibilitySetting(context);
} catch (ActivityNotFoundException e) {
GlobalAppContext.toast(context.getString(R.string.go_to_accessibility_settings) + context.getString(R.string._app_name));
GlobalAppContext.toast(context.getString(R.string.go_to_accessibility_settings) + context.getString(R.string.app_name));
}
}

View File

@@ -158,7 +158,7 @@ public class MainActivity extends BaseActivity implements OnActivityResultDelega
private void setUpToolbar() {
Toolbar toolbar = $(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setTitle(R.string._app_name);
toolbar.setTitle(R.string.app_name);
ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, toolbar, R.string.text_drawer_open,
R.string.text_drawer_close);
drawerToggle.syncState();

View File

@@ -26,7 +26,7 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/ToolBarStyle"
android:title="@string/_app_name"
android:title="@string/app_name"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay"/>

View File

@@ -19,7 +19,7 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:title="@string/_app_name">
app:title="@string/app_name">
</com.stardust.theme.widget.ThemeColorToolbar>

View File

@@ -17,7 +17,7 @@
android:layout_width="match_parent"
android:theme="@style/ToolBarStyle"
android:layout_height="?attr/actionBarSize"
android:title="@string/_app_name"
android:title="@string/app_name"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay"/>

View File

@@ -19,7 +19,7 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:title="@string/_app_name">
app:title="@string/app_name">
</com.stardust.theme.widget.ThemeColorToolbar>

View File

@@ -19,7 +19,7 @@
android:theme="@style/ToolBarStyle"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:title="@string/_app_name"
android:title="@string/app_name"
app:popupTheme="@style/AppTheme.PopupOverlay">
<LinearLayout

View File

@@ -51,7 +51,7 @@
android:layout_marginTop="8dp"
android:gravity="center"
android:minHeight="60dp"
android:text="@string/_app_name"
android:text="@string/app_name"
android:textColor="@android:color/primary_text_light"/>
</LinearLayout>

View File

@@ -17,7 +17,7 @@
android:layout_height="?attr/actionBarSize"
android:theme="@style/ToolBarStyle"
android:background="?attr/colorPrimary"
android:title="@string/_app_name"
android:title="@string/app_name"
app:popupTheme="@style/AppTheme.PopupOverlay">
<FrameLayout

View File

@@ -1,5 +1,5 @@
<resources>
<string name="_app_name">Auto.js</string>
<string name="app_name">Auto.js</string>
<string name="text_accessibility_service_description">Required by the script automatic operation (click, long press, slide, etc.).</string>
<string name="text_new_file">New File</string>
<string name="text_create_fail">Creation failed</string>

View File

@@ -1,11 +1,11 @@
<resources>
<string name="_app_name">Auto.js</string>
<string name="app_name">Auto.js</string>
<string name="text_new_file">新建文件</string>
<string name="text_create_fail">创建失败</string>
<string name="text_please_input_name">请输入名称</string>
<string name="text_name">名称</string>
<string name="text_go_to_setting">去设置</string>
<string name="explain_accessibility_permission">软件需要打开\"无障碍服务\"才能运行,请在随后的设置中选择\"AutoJs\"并开启服务。\n您也可以稍后在侧拉菜单中设置。</string>
<string name="explain_accessibility_permission">软件需要打开\"无障碍服务\"才能运行,请在随后的设置中选择\"Auto.js\"并开启服务。\n您也可以稍后在侧拉菜单中设置。</string>
<string name="text_cancel">取消</string>
<string name="text_path_is_empty">路径为空</string>
<string name="text_file_not_exists">文件不存在</string>
@@ -27,7 +27,7 @@
<string name="text_about">关于</string>
<string name="text_licenses">开放源代码许可</string>
<string name="text_do_not_remind_again">不再提示</string>
<string name="copyright">Copyright©2016 All right reserves.</string>
<string name="copyright">Copyright©2018-2019 All right reserves.</string>
<string name="developer">星尘幻影</string>
<string name="qq" translatable="false">2732014414</string>
<string name="email" translatable="false">hybbbb1996@gmail.com</string>
@@ -35,7 +35,7 @@
<string name="text_already_copy_to_clip">已复制到剪贴板</string>
<string name="donate_developer">打赏作者</string>
<string name="my_github">https://github.com/hyb1996/NoRootScriptDroid</string>
<string name="share_app">[AutoJs]下载地址http://www.coolapk.com/apk/org.autojs.autojs </string>
<string name="share_app">[Auto.js]下载地址http://www.coolapk.com/apk/org.autojs.autojs </string>
<string name="text_floating_window">悬浮窗</string>
<string name="text_error_report">错误报告</string>
<string name="text_press_again_to_exit">再按一次退出程序</string>
@@ -99,7 +99,7 @@
<string name="text_import_succeed">导入成功</string>
<string name="text_current_activity">当前活动:</string>
<string name="text_current_package">当前应用包名:</string>
<string name="go_to_accessibility_settings"><![CDATA[请打开设置->无障碍服务->AutoJs并开启]]></string>
<string name="go_to_accessibility_settings"><![CDATA[请打开设置->无障碍服务->Auto.js并开启]]></string>
<string name="text_script_running">脚本运行</string>
<string name="key_use_volume_control_running">key_use_volume_control_running</string>
<string name="text_use_volume_to_stop_running">音量上键停止所有脚本</string>

View File

@@ -32,7 +32,7 @@
<service
android:name="com.stardust.autojs.core.accessibility.AccessibilityService"
android:label="@string/_app_name"
android:label="@string/app_name"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>

View File

@@ -5,15 +5,12 @@ import android.view.View;
import com.stardust.autojs.BuildConfig;
import com.stardust.autojs.core.ui.ViewExtras;
import com.stardust.autojs.rhino.NativeJavaObjectWithPrototype;
import com.stardust.autojs.engine.module.AssetAndUrlModuleSourceProvider;
import com.stardust.autojs.rhino.RhinoAndroidHelper;
import com.stardust.autojs.rhino.TokenStream;
import com.stardust.autojs.rhino.TopLevelScope;
import com.stardust.autojs.runtime.ScriptRuntime;
import com.stardust.autojs.script.JavaScriptSource;
import com.stardust.autojs.script.StringScriptSource;
import com.stardust.automator.UiObjectCollection;
import com.stardust.pio.PFiles;
import com.stardust.pio.UncheckedIOException;
import org.mozilla.javascript.Context;

View File

@@ -0,0 +1,14 @@
package com.stardust.autojs.engine.encryption
import com.stardust.util.AdvancedEncryptionStandard
object ScriptEncryption {
private var mKey = ""
private var mInitVector = ""
fun decrypt(bytes: ByteArray, start: Int = 0, end: Int = bytes.size): ByteArray {
return AdvancedEncryptionStandard(mKey.toByteArray(), mInitVector).decrypt(bytes, start, end)
}
}

View File

@@ -1,20 +1,21 @@
package com.stardust.autojs.engine;
package com.stardust.autojs.engine.module;
import android.content.res.AssetManager;
import android.net.Uri;
import org.mozilla.javascript.Scriptable;
import com.stardust.autojs.engine.encryption.ScriptEncryption;
import com.stardust.autojs.script.EncryptedScriptFileHeader;
import org.mozilla.javascript.commonjs.module.provider.ModuleSource;
import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider;
import java.io.File;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.net.URLConnection;
import java.util.List;
/**
@@ -49,4 +50,17 @@ public class AssetAndUrlModuleSourceProvider extends UrlModuleSourceProvider {
return super.loadFromPrivilegedLocations(moduleId, validator);
}
}
@Override
protected Reader getReader(URLConnection urlConnection) throws IOException {
InputStream stream = urlConnection.getInputStream();
byte[] bytes = new byte[stream.available()];
stream.read(bytes);
stream.close();
if (EncryptedScriptFileHeader.INSTANCE.isValidFile(bytes)) {
byte[] clearText = ScriptEncryption.INSTANCE.decrypt(bytes, EncryptedScriptFileHeader.BLOCK_SIZE, bytes.length);
return new InputStreamReader(new ByteArrayInputStream(clearText));
}
return new InputStreamReader(new ByteArrayInputStream(bytes));
}
}

View File

@@ -0,0 +1,364 @@
package com.stardust.autojs.engine.module;
import org.mozilla.javascript.commonjs.module.provider.DefaultUrlConnectionExpiryCalculator;
import org.mozilla.javascript.commonjs.module.provider.ModuleSource;
import org.mozilla.javascript.commonjs.module.provider.ModuleSourceProviderBase;
import org.mozilla.javascript.commonjs.module.provider.ParsedContentType;
import org.mozilla.javascript.commonjs.module.provider.UrlConnectionExpiryCalculator;
import org.mozilla.javascript.commonjs.module.provider.UrlConnectionSecurityDomainProvider;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.List;
/**
* A URL-based script provider that can load modules against a set of base
* privileged and fallback URIs. It is deliberately not named "URI provider"
* but a "URL provider" since it actually only works against those URIs that
* are URLs (and the JRE has a protocol handler for them). It creates cache
* validators that are suitable for use with both file: and http: URL
* protocols. Specifically, it is able to use both last-modified timestamps and
* ETags for cache revalidation, and follows the HTTP cache expiry calculation
* model, and allows for fallback heuristic expiry calculation when no server
* specified expiry is provided.
*
* @author Attila Szegedi
* @version $Id: UrlModuleSourceProvider.java,v 1.4 2011/04/07 20:26:12 hannes%helma.at Exp $
*/
public class UrlModuleSourceProvider extends ModuleSourceProviderBase {
private static final long serialVersionUID = 1L;
private final Iterable<URI> privilegedUris;
private final Iterable<URI> fallbackUris;
private final UrlConnectionSecurityDomainProvider
urlConnectionSecurityDomainProvider;
private final UrlConnectionExpiryCalculator urlConnectionExpiryCalculator;
/**
* Creates a new module script provider that loads modules against a set of
* privileged and fallback URIs. It will use a fixed default cache expiry
* of 60 seconds, and provide no security domain objects for the resource.
*
* @param privilegedUris an iterable providing the privileged URIs. Can be
* null if no privileged URIs are used.
* @param fallbackUris an iterable providing the fallback URIs. Can be
* null if no fallback URIs are used.
*/
public UrlModuleSourceProvider(Iterable<URI> privilegedUris,
Iterable<URI> fallbackUris) {
this(privilegedUris, fallbackUris,
new DefaultUrlConnectionExpiryCalculator(), null);
}
/**
* Creates a new module script provider that loads modules against a set of
* privileged and fallback URIs. It will use the specified heuristic cache
* expiry calculator and security domain provider.
*
* @param privilegedUris an iterable providing the privileged URIs. Can be
* null if no privileged URIs are used.
* @param fallbackUris an iterable providing the fallback URIs. Can be
* null if no fallback URIs are used.
* @param urlConnectionExpiryCalculator the calculator object for heuristic
* calculation of the resource expiry, used when no expiry is provided by
* the server of the resource. Can be null, in which case the maximum age
* of cached entries without validation will be zero.
* @param urlConnectionSecurityDomainProvider object that provides security
* domain objects for the loaded sources. Can be null, in which case the
* loaded sources will have no security domain associated with them.
*/
public UrlModuleSourceProvider(Iterable<URI> privilegedUris,
Iterable<URI> fallbackUris,
UrlConnectionExpiryCalculator urlConnectionExpiryCalculator,
UrlConnectionSecurityDomainProvider urlConnectionSecurityDomainProvider) {
this.privilegedUris = privilegedUris;
this.fallbackUris = fallbackUris;
this.urlConnectionExpiryCalculator = urlConnectionExpiryCalculator;
this.urlConnectionSecurityDomainProvider =
urlConnectionSecurityDomainProvider;
}
@Override
protected ModuleSource loadFromPrivilegedLocations(
String moduleId, Object validator)
throws IOException, URISyntaxException {
return loadFromPathList(moduleId, validator, privilegedUris);
}
@Override
protected ModuleSource loadFromFallbackLocations(
String moduleId, Object validator)
throws IOException, URISyntaxException {
return loadFromPathList(moduleId, validator, fallbackUris);
}
private ModuleSource loadFromPathList(String moduleId,
Object validator, Iterable<URI> paths)
throws IOException, URISyntaxException {
if (paths == null) {
return null;
}
for (URI path : paths) {
final ModuleSource moduleSource = loadFromUri(
path.resolve(moduleId), path, validator);
if (moduleSource != null) {
return moduleSource;
}
}
return null;
}
@Override
protected ModuleSource loadFromUri(URI uri, URI base, Object validator)
throws IOException, URISyntaxException {
// We expect modules to have a ".js" file name extension ...
URI fullUri = new URI(uri + ".js");
ModuleSource source = loadFromActualUri(fullUri, base, validator);
// ... but for compatibility we support modules without extension,
// or ids with explicit extension.
return source != null ?
source : loadFromActualUri(uri, base, validator);
}
protected ModuleSource loadFromActualUri(URI uri, URI base, Object validator)
throws IOException {
final URL url = new URL(base == null ? null : base.toURL(), uri.toString());
final long request_time = System.currentTimeMillis();
final URLConnection urlConnection = openUrlConnection(url);
final URLValidator applicableValidator;
if (validator instanceof URLValidator) {
final URLValidator uriValidator = ((URLValidator) validator);
applicableValidator = uriValidator.appliesTo(uri) ? uriValidator :
null;
} else {
applicableValidator = null;
}
if (applicableValidator != null) {
applicableValidator.applyConditionals(urlConnection);
}
try {
urlConnection.connect();
if (applicableValidator != null &&
applicableValidator.updateValidator(urlConnection,
request_time, urlConnectionExpiryCalculator)) {
close(urlConnection);
return NOT_MODIFIED;
}
return new ModuleSource(getReader(urlConnection),
getSecurityDomain(urlConnection), uri, base,
new URLValidator(uri, urlConnection, request_time,
urlConnectionExpiryCalculator));
} catch (FileNotFoundException e) {
return null;
} catch (RuntimeException e) {
close(urlConnection);
throw e;
} catch (IOException e) {
close(urlConnection);
throw e;
}
}
protected Reader getReader(URLConnection urlConnection)
throws IOException {
return new InputStreamReader(urlConnection.getInputStream(),
getCharacterEncoding(urlConnection));
}
protected String getCharacterEncoding(URLConnection urlConnection) {
final ParsedContentType pct = new ParsedContentType(
urlConnection.getContentType());
final String encoding = pct.getEncoding();
if (encoding != null) {
return encoding;
}
final String contentType = pct.getContentType();
if (contentType != null && contentType.startsWith("text/")) {
return "8859_1";
}
return "utf-8";
}
protected Object getSecurityDomain(URLConnection urlConnection) {
return urlConnectionSecurityDomainProvider == null ? null :
urlConnectionSecurityDomainProvider.getSecurityDomain(
urlConnection);
}
private void close(URLConnection urlConnection) {
try {
urlConnection.getInputStream().close();
} catch (IOException e) {
onFailedClosingUrlConnection(urlConnection, e);
}
}
/**
* Override if you want to get notified if the URL connection fails to
* close. Does nothing by default.
*
* @param urlConnection the connection
* @param cause the cause it failed to close.
*/
protected void onFailedClosingUrlConnection(URLConnection urlConnection,
IOException cause) {
}
/**
* Can be overridden in subclasses to customize the URL connection opening
* process. By default, just calls {@link URL#openConnection()}.
*
* @param url the URL
* @return a connection to the URL.
* @throws IOException if an I/O error occurs.
*/
protected URLConnection openUrlConnection(URL url) throws IOException {
return url.openConnection();
}
@Override
protected boolean entityNeedsRevalidation(Object validator) {
return !(validator instanceof URLValidator)
|| ((URLValidator) validator).entityNeedsRevalidation();
}
private static class URLValidator implements Serializable {
private static final long serialVersionUID = 1L;
private final URI uri;
private final long lastModified;
private final String entityTags;
private long expiry;
public URLValidator(URI uri, URLConnection urlConnection,
long request_time, UrlConnectionExpiryCalculator
urlConnectionExpiryCalculator) {
this.uri = uri;
this.lastModified = urlConnection.getLastModified();
this.entityTags = getEntityTags(urlConnection);
expiry = calculateExpiry(urlConnection, request_time,
urlConnectionExpiryCalculator);
}
boolean updateValidator(URLConnection urlConnection, long request_time,
UrlConnectionExpiryCalculator urlConnectionExpiryCalculator)
throws IOException {
boolean isResourceChanged = isResourceChanged(urlConnection);
if (!isResourceChanged) {
expiry = calculateExpiry(urlConnection, request_time,
urlConnectionExpiryCalculator);
}
return isResourceChanged;
}
private boolean isResourceChanged(URLConnection urlConnection)
throws IOException {
if (urlConnection instanceof HttpURLConnection) {
return ((HttpURLConnection) urlConnection).getResponseCode() ==
HttpURLConnection.HTTP_NOT_MODIFIED;
}
return lastModified != urlConnection.getLastModified();
}
private long calculateExpiry(URLConnection urlConnection,
long request_time, UrlConnectionExpiryCalculator
urlConnectionExpiryCalculator) {
if ("no-cache".equals(urlConnection.getHeaderField("Pragma"))) {
return 0L;
}
final String cacheControl = urlConnection.getHeaderField(
"Cache-Control");
if (cacheControl != null) {
if (cacheControl.indexOf("no-cache") != -1) {
return 0L;
}
final int max_age = getMaxAge(cacheControl);
if (-1 != max_age) {
final long response_time = System.currentTimeMillis();
final long apparent_age = Math.max(0, response_time -
urlConnection.getDate());
final long corrected_received_age = Math.max(apparent_age,
urlConnection.getHeaderFieldInt("Age", 0) * 1000L);
final long response_delay = response_time - request_time;
final long corrected_initial_age = corrected_received_age +
response_delay;
final long creation_time = response_time -
corrected_initial_age;
return max_age * 1000L + creation_time;
}
}
final long explicitExpiry = urlConnection.getHeaderFieldDate(
"Expires", -1L);
if (explicitExpiry != -1L) {
return explicitExpiry;
}
return urlConnectionExpiryCalculator == null ? 0L :
urlConnectionExpiryCalculator.calculateExpiry(urlConnection);
}
private int getMaxAge(String cacheControl) {
final int maxAgeIndex = cacheControl.indexOf("max-age");
if (maxAgeIndex == -1) {
return -1;
}
final int eq = cacheControl.indexOf('=', maxAgeIndex + 7);
if (eq == -1) {
return -1;
}
final int comma = cacheControl.indexOf(',', eq + 1);
final String strAge;
if (comma == -1) {
strAge = cacheControl.substring(eq + 1);
} else {
strAge = cacheControl.substring(eq + 1, comma);
}
try {
return Integer.parseInt(strAge);
} catch (NumberFormatException e) {
return -1;
}
}
private String getEntityTags(URLConnection urlConnection) {
final List<String> etags = urlConnection.getHeaderFields().get("ETag");
if (etags == null || etags.isEmpty()) {
return null;
}
final StringBuilder b = new StringBuilder();
final Iterator<String> it = etags.iterator();
b.append(it.next());
while (it.hasNext()) {
b.append(", ").append(it.next());
}
return b.toString();
}
boolean appliesTo(URI uri) {
return this.uri.equals(uri);
}
void applyConditionals(URLConnection urlConnection) {
if (lastModified != 0L) {
urlConnection.setIfModifiedSince(lastModified);
}
if (entityTags != null && entityTags.length() > 0) {
urlConnection.addRequestProperty("If-None-Match", entityTags);
}
}
boolean entityNeedsRevalidation() {
return System.currentTimeMillis() > expiry;
}
}
}

View File

@@ -12,7 +12,9 @@ data class ExecutionConfig(var workingDirectory: String = "",
var intentFlags: Int = 0,
var delay: Long = 0,
var interval: Long = 0,
var loopTimes: Int = 1) : Parcelable {
var loopTimes: Int = 1,
var uiMode: Boolean = false,
var features: Int = 0) : Parcelable {
private val mArguments = HashMap<String, Any>()
@@ -79,13 +81,16 @@ data class ExecutionConfig(var workingDirectory: String = "",
companion object CREATOR : Parcelable.Creator<ExecutionConfig> {
@JvmStatic
val tag = "execution.config"
@JvmStatic
val tag = "execution.config"
@JvmStatic
val default: ExecutionConfig
get() = ExecutionConfig()
@JvmStatic
val featureContinuation = 1
override fun createFromParcel(parcel: Parcel): ExecutionConfig {
return ExecutionConfig(parcel)
}

View File

@@ -118,8 +118,8 @@ public class ScriptExecuteActivity extends AppCompatActivity {
private void prepare() {
mScriptEngine.put("activity", this);
mScriptEngine.setTag("activity", this);
mScriptEngine.setTag(ScriptEngine.TAG_ENV_PATH, mScriptExecution.getConfig().getWorkingDirectory());
mScriptEngine.setTag(ScriptEngine.TAG_WORKING_DIRECTORY, mScriptExecution.getConfig().getPath());
mScriptEngine.setTag(ScriptEngine.TAG_ENV_PATH, mScriptExecution.getConfig().getPath());
mScriptEngine.setTag(ScriptEngine.TAG_WORKING_DIRECTORY, mScriptExecution.getConfig().getWorkingDirectory());
mScriptEngine.init();
}

View File

@@ -0,0 +1,47 @@
package com.stardust.autojs.script
import java.io.File
import java.io.FileInputStream
import java.io.OutputStream
object EncryptedScriptFileHeader {
const val FLAG_INVALID_FILE: Short = Short.MIN_VALUE
const val FLAG_EXECUTION_MODE_UI: Short = 0x0001
const val FLAG_EXECUTION_MODE_AUTO: Short = 0x0002
const val BLOCK_SIZE = 8
private val BLOCK = byteArrayOf(0x77, 0x01, 0x17, 0x7F, 0x12, 0x12)
fun getHeaderFlags(file: File): Short {
val fis = FileInputStream(file)
val bytes = ByteArray(BLOCK_SIZE)
if (fis.read(bytes) < BLOCK_SIZE) {
return FLAG_INVALID_FILE
}
if (!isValidFile(bytes)) {
return FLAG_INVALID_FILE
}
return (bytes[BLOCK.size].toShort() * 256 + bytes[BLOCK.size + 1]).toShort()
}
fun isValidFile(bytes: ByteArray): Boolean {
for (i in 0 until BLOCK.size) {
if (bytes[i] != BLOCK[i]) {
return false
}
}
return true
}
fun writeHeader(os: OutputStream, flags: Short = 0) {
os.write(BLOCK)
val byte6 = flags / 256
val byte7 = flags % 256
os.write(byte6)
os.write(byte7)
}
}

View File

@@ -43,6 +43,15 @@ public class JavaScriptFileSource extends JavaScriptSource {
return mScript;
}
@Override
protected int parseExecutionMode() {
short flags = EncryptedScriptFileHeader.INSTANCE.getHeaderFlags(mFile);
if (flags == EncryptedScriptFileHeader.FLAG_INVALID_FILE) {
return super.parseExecutionMode();
}
return flags;
}
@Override
public Reader getScriptReader() {
try {

View File

@@ -1,10 +1,15 @@
package com.stardust.autojs.script;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.stardust.autojs.rhino.TokenStream;
import com.stardust.util.MapBuilder;
import org.mozilla.javascript.Token;
import java.io.Reader;
import java.io.StringReader;
import java.util.Map;
@@ -23,11 +28,13 @@ public abstract class JavaScriptSource extends ScriptSource {
public static final int EXECUTION_MODE_UI = 0x00000001;
public static final int EXECUTION_MODE_AUTO = 0x00000002;
private static final String LOG_TAG = "JavaScriptSource";
private static final Map<String, Integer> EXECUTION_MODES = new MapBuilder<String, Integer>()
.put("ui", EXECUTION_MODE_UI)
.put("auto", EXECUTION_MODE_AUTO)
.build();
private static final int EXECUTION_MODE_STRING_MAX_LENGTH = 7;
private static final int PARSING_MAX_TOKEN = 300;
private int mExecutionMode = -1;
@@ -57,20 +64,35 @@ public abstract class JavaScriptSource extends ScriptSource {
public int getExecutionMode() {
if (mExecutionMode == -1) {
mExecutionMode = parseExecutionMode(getScript());
mExecutionMode = parseExecutionMode();
}
return mExecutionMode;
}
private int parseExecutionMode(String script) {
if (script == null || script.length() == 0)
return EXECUTION_MODE_NORMAL;
if(script.charAt(0) == '"'){
int i = script.lastIndexOf("\";", EXECUTION_MODE_STRING_MAX_LENGTH + 2);
if (i >= 0){
String modeString = script.substring(1, i);
return parseExecutionMode(modeString.split(" "));
protected int parseExecutionMode() {
String script = getScript();
TokenStream ts = new TokenStream(new StringReader(script), null, 1);
int token;
int count = 0;
try {
while (count <= PARSING_MAX_TOKEN && (token = ts.getToken()) != Token.EOF) {
count++;
if (token == Token.EOL || token == Token.COMMENT) {
continue;
}
if (token == Token.STRING && ts.getTokenLength() > 2) {
String tokenString = script.substring(ts.getTokenBeg() + 1, ts.getTokenEnd() - 1);
if (ts.getToken() != Token.SEMI) {
break;
}
Log.d(LOG_TAG, "string = " + tokenString);
return parseExecutionMode(tokenString.split(" "));
}
break;
}
} catch (Exception e) {
e.printStackTrace();
return EXECUTION_MODE_NORMAL;
}
return EXECUTION_MODE_NORMAL;

View File

@@ -1,10 +1,9 @@
package com.stardust.autojs.script;
import android.content.Context;
import android.util.Log;
import android.view.View;
import com.stardust.autojs.engine.AssetAndUrlModuleSourceProvider;
import com.stardust.autojs.engine.module.AssetAndUrlModuleSourceProvider;
import com.stardust.pio.PFiles;
import com.stardust.pio.UncheckedIOException;
@@ -18,7 +17,6 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

View File

@@ -1,5 +1,5 @@
<resources>
<string name="app_name">AutoJs</string>
<string name="app_name">Auto.js</string>
<string name="text_error">Error:</string>
<string name="text_start_running">Script Running</string>
<string name="text_path_is_empty">Path is empty</string>
@@ -13,7 +13,6 @@
<string name="text_console">Console</string>
<string name="text_no_floating_window_permission">No drawing overlay permission</string>
<string name="text_accessibility_service_description">Auto.js</string>
<string name="_app_name">AutoJs</string>
<string name="text_should_enable_key_observing">Key observing is disabled, please enable in settings</string>
<string name="no_write_settings_permissin">No writing settings permission</string>
<string name="exception_notification_service_disabled">通知服务未运行,请重新启用通知权限</string>

View File

@@ -1,5 +1,5 @@
<resources>
<string name="app_name">AutoJs</string>
<string name="app_name">Auto.js</string>
<string name="text_error">错误:</string>
<string name="text_start_running">开始运行</string>
<string name="text_path_is_empty">路径为空</string>
@@ -13,7 +13,6 @@
<string name="text_console">控制台</string>
<string name="text_no_floating_window_permission">没有悬浮窗权限</string>
<string name="text_accessibility_service_description">使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。</string>
<string name="_app_name">AutoJs</string>
<string name="text_should_enable_key_observing">按键监听未启用,请在软件设置中开启</string>
<string name="text_should_enable_gesture_observing">手势监听未启用,请在软件设置中开启</string>
<string name="no_write_settings_permissin">沒有修改系統设置权限</string>

View File

@@ -31,7 +31,7 @@ class AdvancedEncryptionStandard(private val key: ByteArray, private val initVec
val ivParameterSpec = IvParameterSpec(initVector.toByteArray())
val cipher = Cipher.getInstance(FULL_ALGORITHM)
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec)
return cipher.doFinal(cipherText, start, end)
return cipher.doFinal(cipherText, start, end - start)
}
companion object {

View File

@@ -23,8 +23,6 @@ import java.lang.IllegalStateException
*/
class AutoJs private constructor(application: Application) : com.stardust.autojs.AutoJs(application) {
private var mKey = ""
private var mInitVector = ""
init {
scriptEngineService.registerGlobalScriptExecutionListener(ScriptExecutionGlobalListener())
@@ -85,7 +83,6 @@ class AutoJs private constructor(application: Application) : com.stardust.autojs
super.initScriptEngineManager()
scriptEngineManager.registerEngine(JavaScriptSource.ENGINE) {
val engine = XJavaScriptEngine(application)
engine.setKey(mKey, mInitVector)
engine.runtime = createRuntime()
engine
}
@@ -98,11 +95,6 @@ class AutoJs private constructor(application: Application) : com.stardust.autojs
return runtime
}
fun setKey(key: String, vet: String) {
mKey = key
mInitVector = vet
}
companion object {
@SuppressLint("StaticFieldLeak")

View File

@@ -20,7 +20,7 @@ class ScriptExecutionGlobalListener : ScriptExecutionListener {
}
private fun onFinish(execution: ScriptExecution) {
val millis = execution.engine.getTag(ENGINE_TAG_START_TIME) as Long ?: return
val millis = execution.engine.getTag(ENGINE_TAG_START_TIME) as Long? ?: return
val seconds = (System.currentTimeMillis() - millis) / 1000.0
AutoJs.instance.scriptEngineService.globalConsole
.verbose(GlobalAppContext.getString(R.string.text_execution_finished), execution.source.toString(), seconds)

View File

@@ -2,20 +2,17 @@ package com.stardust.auojs.inrt.autojs
import android.content.Context
import com.stardust.autojs.engine.LoopBasedJavaScriptEngine
import com.stardust.autojs.engine.encryption.ScriptEncryption
import com.stardust.autojs.script.EncryptedScriptFileHeader
import com.stardust.autojs.script.JavaScriptFileSource
import com.stardust.autojs.script.ScriptSource
import com.stardust.autojs.script.StringScriptSource
import com.stardust.pio.PFiles
import com.stardust.util.AdvancedEncryptionStandard
import java.io.File
import java.lang.Exception
import java.lang.IllegalStateException
import java.security.GeneralSecurityException
class XJavaScriptEngine(context: Context) : LoopBasedJavaScriptEngine(context) {
private var mKey = ""
private var mInitVector = ""
override fun execute(source: ScriptSource, callback: ExecuteCallback?) {
if (source is JavaScriptFileSource) {
@@ -32,17 +29,10 @@ class XJavaScriptEngine(context: Context) : LoopBasedJavaScriptEngine(context) {
private fun execute(file: File) {
val bytes = PFiles.readBytes(file.path)
try {
val source = AdvancedEncryptionStandard(mKey.toByteArray(), mInitVector).decrypt(bytes)
super.execute(StringScriptSource(file.name, String(source)))
super.execute(StringScriptSource(file.name, String(ScriptEncryption.decrypt(bytes, EncryptedScriptFileHeader.BLOCK_SIZE))))
} catch (e: GeneralSecurityException) {
e.printStackTrace()
}
}
fun setKey(key: String, initVector: String) {
mKey = key
mInitVector = initVector
}
}

View File

@@ -11,6 +11,7 @@ import com.stardust.auojs.inrt.BuildConfig
import com.stardust.auojs.inrt.LogActivity
import com.stardust.auojs.inrt.Pref
import com.stardust.auojs.inrt.autojs.AutoJs
import com.stardust.autojs.engine.encryption.ScriptEncryption
import com.stardust.autojs.execution.ExecutionConfig
import com.stardust.autojs.execution.ScriptExecution
import com.stardust.autojs.project.ProjectConfig
@@ -101,12 +102,12 @@ open class AssetsProjectLauncher(private val mAssetsProjectDir: String, private
val key = MD5.md5(projectConfig.packageName + projectConfig.versionName + projectConfig.mainScriptFile)
val vec = MD5.md5(projectConfig.buildInfo.buildId + projectConfig.name).substring(0, 16)
try {
val fieldKey = AutoJs::class.java.getDeclaredField("mKey")
val fieldKey = ScriptEncryption::class.java.getDeclaredField("mKey")
fieldKey.isAccessible = true
fieldKey.set(AutoJs.instance, key)
val fieldVector = AutoJs::class.java.getDeclaredField("mInitVector")
fieldKey.set(null, key)
val fieldVector = ScriptEncryption::class.java.getDeclaredField("mInitVector")
fieldVector.isAccessible = true
fieldVector.set(AutoJs.instance, vec)
fieldVector.set(null, vec)
} catch (e: Exception) {
e.printStackTrace()
}