Load libraries directly from apk
Introduced new 'extractNativeLibs' attribute to manifest/application.
Setting it to false prevents installer from extracting library from apk.
The default value for extractNativeLibs is true.
Bug: 8076853
Change-Id: I1aa2c039bb2a590ae72f256acc9ba5401c2c59b1
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 29befc8..8f17845 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -346,6 +346,11 @@
public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27;
/**
+ * When set installer extracts native libs from .apk files.
+ */
+ public static final int FLAG_EXTRACT_NATIVE_LIBS = 1<<28;
+
+ /**
* Value for {@link #flags}: true if code from this application will need to be
* loaded into other applications' processes. On devices that support multiple
* instruction sets, this implies the code might be loaded into a process that's
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c443ff3..88a89a9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -269,6 +269,7 @@
public final boolean coreApp;
public final boolean multiArch;
+ public final boolean extractNativeLibs;
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
String[] splitCodePaths, int[] splitRevisionCodes) {
@@ -284,6 +285,7 @@
this.splitRevisionCodes = splitRevisionCodes;
this.coreApp = baseApk.coreApp;
this.multiArch = baseApk.multiArch;
+ this.extractNativeLibs = baseApk.extractNativeLibs;
}
public List<String> getAllCodePaths() {
@@ -310,10 +312,12 @@
public final Signature[] signatures;
public final boolean coreApp;
public final boolean multiArch;
+ public final boolean extractNativeLibs;
public ApkLite(String codePath, String packageName, String splitName, int versionCode,
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
- Signature[] signatures, boolean coreApp, boolean multiArch) {
+ Signature[] signatures, boolean coreApp, boolean multiArch,
+ boolean extractNativeLibs) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -324,6 +328,7 @@
this.signatures = signatures;
this.coreApp = coreApp;
this.multiArch = multiArch;
+ this.extractNativeLibs = extractNativeLibs;
}
}
@@ -1270,6 +1275,7 @@
int revisionCode = 0;
boolean coreApp = false;
boolean multiArch = false;
+ boolean extractNativeLibs = true;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
final String attr = attrs.getAttributeName(i);
@@ -1308,14 +1314,17 @@
final String attr = attrs.getAttributeName(i);
if ("multiArch".equals(attr)) {
multiArch = attrs.getAttributeBooleanValue(i, false);
- break;
+ }
+ if ("extractNativeLibs".equals(attr)) {
+ extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
}
}
}
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
- revisionCode, installLocation, verifiers, signatures, coreApp, multiArch);
+ revisionCode, installLocation, verifiers, signatures, coreApp, multiArch,
+ extractNativeLibs);
}
/**
@@ -2569,6 +2578,12 @@
ai.flags |= ApplicationInfo.FLAG_MULTIARCH;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs,
+ true)) {
+ ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
+ }
+
String str;
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 02f675c..f479f4f 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -33,6 +33,7 @@
import android.content.pm.PackageParser.PackageParserException;
import android.os.Build;
import android.os.SELinux;
+import android.os.SystemProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Slog;
@@ -74,6 +75,7 @@
final long[] apkHandles;
final boolean multiArch;
+ final boolean extractNativeLibs;
public static Handle create(File packageFile) throws IOException {
try {
@@ -86,14 +88,16 @@
public static Handle create(Package pkg) throws IOException {
return create(pkg.getAllCodePaths(),
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0);
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0,
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0);
}
public static Handle create(PackageLite lite) throws IOException {
- return create(lite.getAllCodePaths(), lite.multiArch);
+ return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs);
}
- private static Handle create(List<String> codePaths, boolean multiArch) throws IOException {
+ private static Handle create(List<String> codePaths, boolean multiArch,
+ boolean extractNativeLibs) throws IOException {
final int size = codePaths.size();
final long[] apkHandles = new long[size];
for (int i = 0; i < size; i++) {
@@ -108,12 +112,13 @@
}
}
- return new Handle(apkHandles, multiArch);
+ return new Handle(apkHandles, multiArch, extractNativeLibs);
}
- Handle(long[] apkHandles, boolean multiArch) {
+ Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs) {
this.apkHandles = apkHandles;
this.multiArch = multiArch;
+ this.extractNativeLibs = extractNativeLibs;
mGuard.open("close");
}
@@ -146,8 +151,8 @@
private static native long nativeSumNativeBinaries(long handle, String cpuAbi);
- private native static int nativeCopyNativeBinaries(long handle,
- String sharedLibraryPath, String abiToCopy);
+ private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
+ String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge);
private static long sumNativeBinaries(Handle handle, String abi) {
long sum = 0;
@@ -167,7 +172,8 @@
*/
public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
for (long apkHandle : handle.apkHandles) {
- int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi);
+ int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
+ handle.extractNativeLibs, HAS_NATIVE_BRIDGE);
if (res != INSTALL_SUCCEEDED) {
return res;
}
@@ -218,7 +224,8 @@
/**
* Remove the native binaries of a given package. This deletes the files
*/
- public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir) {
+ public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
+ boolean deleteRootDir) {
if (DEBUG_NATIVE) {
Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
}
@@ -247,7 +254,8 @@
// asked to or this will prevent installation of future updates.
if (deleteRootDir) {
if (!nativeLibraryRoot.delete()) {
- Slog.w(TAG, "Could not delete native binary directory: " + nativeLibraryRoot.getPath());
+ Slog.w(TAG, "Could not delete native binary directory: " +
+ nativeLibraryRoot.getPath());
}
}
}
@@ -416,6 +424,9 @@
// We don't care about the other return values for now.
private static final int BITCODE_PRESENT = 1;
+ private static final boolean HAS_NATIVE_BRIDGE =
+ !"0".equals(SystemProperties.get("ro.dalvik.vm.native.bridge", "0"));
+
private static native int hasRenderscriptBitcode(long apkHandle);
public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 441af15..34de6c5 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -537,7 +537,6 @@
*/
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
- int result = -1;
JavaVMInitArgs initArgs;
char propBuf[PROPERTY_VALUE_MAX];
char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];
@@ -581,6 +580,7 @@
char localeOption[sizeof("-Duser.locale=") + PROPERTY_VALUE_MAX];
char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX];
char nativeBridgeLibrary[sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX];
+ char cpuAbiListBuf[sizeof("--cpu-abilist=") + PROPERTY_VALUE_MAX];
bool checkJni = false;
property_get("dalvik.vm.checkjni", propBuf, "");
@@ -699,7 +699,7 @@
if (!hasFile("/system/etc/preloaded-classes")) {
ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n",
strerror(errno));
- goto bail;
+ return -1;
}
addOption("-Ximage-compiler-option");
addOption("--image-classes=/system/etc/preloaded-classes");
@@ -811,6 +811,19 @@
addOption(nativeBridgeLibrary);
}
+#if defined(__LP64__)
+ const char* cpu_abilist_property_name = "ro.product.cpu.abilist64";
+#else
+ const char* cpu_abilist_property_name = "ro.product.cpu.abilist32";
+#endif // defined(__LP64__)
+ property_get(cpu_abilist_property_name, propBuf, "");
+ if (propBuf[0] == '\0') {
+ ALOGE("%s is not expected to be empty", cpu_abilist_property_name);
+ return -1;
+ }
+ snprintf(cpuAbiListBuf, sizeof(cpuAbiListBuf), "--cpu-abilist=%s", propBuf);
+ addOption(cpuAbiListBuf);
+
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
@@ -825,13 +838,10 @@
*/
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
- goto bail;
+ return -1;
}
- result = 0;
-
-bail:
- return result;
+ return 0;
}
char* AndroidRuntime::toSlashClassName(const char* className)
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 3c1993e..9307ff9 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -33,6 +33,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -173,7 +174,11 @@
static install_status_t
copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
{
- jstring* javaNativeLibPath = (jstring*) arg;
+ void** args = reinterpret_cast<void**>(arg);
+ jstring* javaNativeLibPath = (jstring*) args[0];
+ jboolean extractNativeLibs = *(jboolean*) args[1];
+ jboolean hasNativeBridge = *(jboolean*) args[2];
+
ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
size_t uncompLen;
@@ -181,13 +186,31 @@
long crc;
time_t modTime;
- if (!zipFile->getEntryInfo(zipEntry, NULL, &uncompLen, NULL, NULL, &when, &crc)) {
+ int method;
+ off64_t offset;
+
+ if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, NULL, &offset, &when, &crc)) {
ALOGD("Couldn't read zip entry info\n");
return INSTALL_FAILED_INVALID_APK;
- } else {
- struct tm t;
- ZipUtils::zipTimeToTimespec(when, &t);
- modTime = mktime(&t);
+ }
+
+ if (!extractNativeLibs) {
+ // check if library is uncompressed and page-aligned
+ if (method != ZipFileRO::kCompressStored) {
+ ALOGD("Library '%s' is compressed - will not be able to open it directly from apk.\n",
+ fileName);
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ if (offset % PAGE_SIZE != 0) {
+ ALOGD("Library '%s' is not page-aligned - will not be able to open it directly from"
+ " apk.\n", fileName);
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ if (!hasNativeBridge) {
+ return INSTALL_SUCCEEDED;
+ }
}
// Build local file path
@@ -208,6 +231,9 @@
}
// Only copy out the native file if it's different.
+ struct tm t;
+ ZipUtils::zipTimeToTimespec(when, &t);
+ modTime = mktime(&t);
struct stat64 st;
if (!isFileDifferent(localFileName, uncompLen, modTime, crc, &st)) {
return INSTALL_SUCCEEDED;
@@ -465,10 +491,12 @@
static jint
com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
- jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi)
+ jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
+ jboolean extractNativeLibs, jboolean hasNativeBridge)
{
+ void* args[] = { &javaNativeLibPath, &extractNativeLibs, &hasNativeBridge };
return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi,
- copyFileIfChanged, &javaNativeLibPath);
+ copyFileIfChanged, reinterpret_cast<void*>(args));
}
static jlong
@@ -548,7 +576,7 @@
"(J)V",
(void *)com_android_internal_content_NativeLibraryHelper_close},
{"nativeCopyNativeBinaries",
- "(JLjava/lang/String;Ljava/lang/String;)I",
+ "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
(void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
{"nativeSumNativeBinaries",
"(JLjava/lang/String;)J",
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c0b2cbef..aefb67c 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1039,6 +1039,10 @@
activity. -->
<attr name="resizeableActivity" format="boolean" />
+ <!-- When set installer will extract native libraries. If set to false
+ libraries in the apk must be stored and page-aligned. -->
+ <attr name="extractNativeLibs" format="boolean"/>
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -1169,8 +1173,8 @@
@hide -->
<attr name="usesCleartextTraffic" />
<attr name="multiArch" />
+ <attr name="extractNativeLibs" />
</declare-styleable>
-
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
features in your package (or other packages). See the
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e507b3d..5e4b039 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2651,4 +2651,5 @@
<public type="attr" name="colorBackgroundFloating" />
+ <public type="attr" name="extractNativeLibs" />
</resources>
diff --git a/core/tests/coretests/apks/install_jni_lib/Android.mk b/core/tests/coretests/apks/install_jni_lib/Android.mk
index b61ea8e..7322e8d 100644
--- a/core/tests/coretests/apks/install_jni_lib/Android.mk
+++ b/core/tests/coretests/apks/install_jni_lib/Android.mk
@@ -23,6 +23,14 @@
libnativehelper
LOCAL_MODULE := libframeworks_coretests_jni
+
+# this does not prevent build system
+# from installing library to /system/lib
LOCAL_MODULE_TAGS := tests
+# .. we want to avoid that... so we put it somewhere
+# bionic linker cant find it without outside help (nativetests):
+LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
index 957fc4a..e0b616c 100644
--- a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
+++ b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
@@ -27,8 +27,8 @@
{ "checkFunction", "()I", (void*) checkFunction },
};
-int register_com_android_framework_coretests_JNITests(JNIEnv* env) {
- return jniRegisterNativeMethods(env, "com/android/framework/coretests/JNITests", sMethods,
+int register_com_android_frameworks_coretests_JNITests(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/frameworks/coretests/JNITests", sMethods,
NELEM(sMethods));
}
@@ -46,7 +46,7 @@
return JNI_ERR;
}
- if ((status = android::register_com_android_framework_coretests_JNITests(e)) < 0) {
+ if ((status = android::register_com_android_frameworks_coretests_JNITests(e)) < 0) {
return JNI_ERR;
}
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
new file mode 100644
index 0000000..5fa2405
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := install_jni_lib_open_from_apk
+
+LOCAL_JNI_SHARED_LIBRARIES_ZIP_OPTIONS := -0
+LOCAL_PAGE_ALIGN_JNI_SHARED_LIBRARIES := true
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml b/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml
new file mode 100644
index 0000000..190f894
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_jni_lib_open_from_apk">
+
+ <application android:hasCode="true" android:label="@string/app_name" android:extractNativeLibs="false">
+ <activity android:name="com.android.frameworks.coretests.OpenFromApkActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml b/core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml
new file mode 100644
index 0000000..8c2a0bf
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name">Load From Apk Test</string>
+</resources>
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java
new file mode 100644
index 0000000..4f9176c
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ *
+ */
+
+package com.android.frameworks.coretests;
+
+public class JNITests {
+ static {
+ System.loadLibrary("frameworks_coretests_jni");
+ }
+
+ public static native int checkFunction();
+}
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java
new file mode 100644
index 0000000..524cad7c
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ *
+ */
+
+package com.android.frameworks.coretests;
+
+import android.app.Activity;
+import android.widget.TextView;
+import android.os.Bundle;
+
+public class OpenFromApkActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ TextView tv = new TextView(this);
+
+ int i = JNITests.checkFunction();
+
+ tv.setText("All is well: i=" + i);
+
+ setContentView(tv);
+ }
+
+}