Record foreign dex files loaded by the app in the profile
A foreign dex file is a file which is not owned by the app
(it's not part of its code paths or its private data directory).
When such a dex file is loaded by the app, the runtime will record
a marker in a dedicated profile folder (foreing_dex_profile_path).
The marker is just a file named after the canonical location of the
dex file where '/' is replaced by '@'.
The markers will be used by the system server system server to
decide if the apk should be fully or profile guide compiled.
Bug: 27334750
Bug: 26080105
(cherry picked from commit 86a9ebe4197e963249ffbbaa1830da97ed642fa5)
Change-Id: I8be1fd4d854fa1e23c3c1054c9c083ad7b27317b
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 3e66ce2..4623a4a 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -194,9 +194,11 @@
}
void Jit::StartProfileSaver(const std::string& filename,
- const std::vector<std::string>& code_paths) {
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir) {
if (save_profiling_info_) {
- ProfileSaver::Start(filename, code_cache_.get(), code_paths);
+ ProfileSaver::Start(filename, code_cache_.get(), code_paths, foreign_dex_profile_path, app_dir);
}
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 109ca3d..570f683 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -70,7 +70,17 @@
return instrumentation_cache_.get();
}
- void StartProfileSaver(const std::string& filename, const std::vector<std::string>& code_paths);
+ // Starts the profile saver if the config options allow profile recording.
+ // The profile will be stored in the specified `filename` and will contain
+ // information collected from the given `code_paths` (a set of dex locations).
+ // The `foreign_dex_profile_path` is the path where the saver will put the
+ // profile markers for loaded dex files which are not owned by the application.
+ // The `app_dir` is the application directory and is used to decide which
+ // dex files belong to the application.
+ void StartProfileSaver(const std::string& filename,
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir);
void StopProfileSaver();
void DumpForSigQuit(std::ostream& os) {
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index ab26f6f..5abfa6c 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -16,6 +16,10 @@
#include "profile_saver.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
#include "art_method-inl.h"
#include "scoped_thread_state_change.h"
#include "oat_file_manager.h"
@@ -42,14 +46,31 @@
ProfileSaver::ProfileSaver(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths)
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir)
: jit_code_cache_(jit_code_cache),
+ foreign_dex_profile_path_(foreign_dex_profile_path),
code_cache_last_update_time_ns_(0),
shutting_down_(false),
first_profile_(true),
wait_lock_("ProfileSaver wait lock"),
period_condition_("ProfileSaver period condition", wait_lock_) {
AddTrackedLocations(output_filename, code_paths);
+ app_data_dir_ = "";
+ if (!app_data_dir.empty()) {
+ // The application directory is used to determine which dex files are owned by app.
+ // Since it could be a symlink (e.g. /data/data instead of /data/user/0), and we
+ // don't have control over how the dex files are actually loaded (symlink or canonical path),
+ // store it's canonical form to be sure we use the same base when comparing.
+ UniqueCPtr<const char[]> app_data_dir_real_path(realpath(app_data_dir.c_str(), nullptr));
+ if (app_data_dir_real_path != nullptr) {
+ app_data_dir_.assign(app_data_dir_real_path.get());
+ } else {
+ LOG(WARNING) << "Failed to get the real path for app dir: " << app_data_dir_
+ << ". The app dir will not be used to determine which dex files belong to the app";
+ }
+ }
}
void ProfileSaver::Run() {
@@ -162,7 +183,9 @@
void ProfileSaver::Start(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths) {
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir) {
DCHECK(Runtime::Current()->UseJit());
DCHECK(!output_filename.empty());
DCHECK(jit_code_cache != nullptr);
@@ -181,7 +204,11 @@
VLOG(profiler) << "Starting profile saver using output file: " << output_filename
<< ". Tracking: " << Join(code_paths, ':');
- instance_ = new ProfileSaver(output_filename, jit_code_cache, code_paths);
+ instance_ = new ProfileSaver(output_filename,
+ jit_code_cache,
+ code_paths,
+ foreign_dex_profile_path,
+ app_data_dir);
// Create a new thread which does the saving.
CHECK_PTHREAD_CALL(
@@ -248,4 +275,83 @@
}
}
+void ProfileSaver::NotifyDexUse(const std::string& dex_location) {
+ std::set<std::string> app_code_paths;
+ std::string foreign_dex_profile_path;
+ std::string app_data_dir;
+ {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ DCHECK(instance_ != nullptr);
+ // Make a copy so that we don't hold the lock while doing I/O.
+ for (const auto& it : instance_->tracked_dex_base_locations_) {
+ app_code_paths.insert(it.second.begin(), it.second.end());
+ }
+ foreign_dex_profile_path = instance_->foreign_dex_profile_path_;
+ app_data_dir = instance_->app_data_dir_;
+ }
+
+ MaybeRecordDexUseInternal(dex_location,
+ app_code_paths,
+ foreign_dex_profile_path,
+ app_data_dir);
+}
+
+void ProfileSaver::MaybeRecordDexUseInternal(
+ const std::string& dex_location,
+ const std::set<std::string>& app_code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir) {
+ if (foreign_dex_profile_path.empty()) {
+ LOG(WARNING) << "Asked to record foreign dex use without a valid profile path ";
+ return;
+ }
+
+ UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr));
+ std::string dex_location_real_path_str(dex_location_real_path.get());
+
+ if (dex_location_real_path_str.compare(0, app_data_dir.length(), app_data_dir) == 0) {
+ // The dex location is under the application folder. Nothing to record.
+ return;
+ }
+
+ if (app_code_paths.find(dex_location) != app_code_paths.end()) {
+ // The dex location belongs to the application code paths. Nothing to record.
+ return;
+ }
+ // Do another round of checks with the real paths.
+ // Note that we could cache all the real locations in the saver (since it's an expensive
+ // operation). However we expect that app_code_paths is small (usually 1 element), and
+ // NotifyDexUse is called just a few times in the app lifetime. So we make the compromise
+ // to save some bytes of memory usage.
+ for (const auto& app_code_location : app_code_paths) {
+ UniqueCPtr<const char[]> real_app_code_location(realpath(app_code_location.c_str(), nullptr));
+ std::string real_app_code_location_str(real_app_code_location.get());
+ if (real_app_code_location_str == dex_location_real_path_str) {
+ // The dex location belongs to the application code paths. Nothing to record.
+ return;
+ }
+ }
+
+ // For foreign dex files we record a flag on disk. PackageManager will (potentially) take this
+ // into account when deciding how to optimize the loaded dex file.
+ // The expected flag name is the canonical path of the apk where '/' is substituted to '@'.
+ // (it needs to be kept in sync with
+ // frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java)
+ std::replace(dex_location_real_path_str.begin(), dex_location_real_path_str.end(), '/', '@');
+ std::string flag_path = foreign_dex_profile_path + "/" + dex_location_real_path_str;
+ // No need to give any sort of access to flag_path. The system has enough permissions
+ // to test for its existence.
+ int fd = TEMP_FAILURE_RETRY(open(flag_path.c_str(), O_CREAT | O_EXCL, 0));
+ if (fd != -1) {
+ if (close(fd) != 0) {
+ PLOG(WARNING) << "Could not close file after flagging foreign dex use " << flag_path;
+ }
+ } else {
+ if (errno != EEXIST) {
+ // Another app could have already created the file.
+ PLOG(WARNING) << "Could not create foreign dex use mark " << flag_path;
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 21017c1..e7eab95 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -30,7 +30,9 @@
// If the saver is already running it adds (output_filename, code_paths) to its tracked locations.
static void Start(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths)
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir)
REQUIRES(!Locks::profiler_lock_, !wait_lock_);
// Stops the profile saver thread.
@@ -42,10 +44,14 @@
// Returns true if the profile saver is started.
static bool IsStarted() REQUIRES(!Locks::profiler_lock_);
+ static void NotifyDexUse(const std::string& dex_location);
+
private:
ProfileSaver(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths);
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir);
// NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
static void* RunProfileSaverThread(void* arg)
@@ -64,6 +70,12 @@
const std::vector<std::string>& code_paths)
REQUIRES(Locks::profiler_lock_);
+ static void MaybeRecordDexUseInternal(
+ const std::string& dex_location,
+ const std::set<std::string>& tracked_locations,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir);
+
// The only instance of the saver.
static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_);
// Profile saver thread.
@@ -72,6 +84,8 @@
jit::JitCodeCache* jit_code_cache_;
SafeMap<std::string, std::set<std::string>> tracked_dex_base_locations_
GUARDED_BY(Locks::profiler_lock_);
+ std::string foreign_dex_profile_path_;
+ std::string app_data_dir_;
uint64_t code_cache_last_update_time_ns_;
bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
bool first_profile_ = true;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index da4a891..f6b2f21 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -566,8 +566,9 @@
static void VMRuntime_registerAppInfo(JNIEnv* env,
jclass clazz ATTRIBUTE_UNUSED,
jstring profile_file,
- jstring app_dir ATTRIBUTE_UNUSED, // TODO: remove argument
- jobjectArray code_paths) {
+ jstring app_dir,
+ jobjectArray code_paths,
+ jstring foreign_dex_profile_path) {
std::vector<std::string> code_paths_vec;
int code_paths_length = env->GetArrayLength(code_paths);
for (int i = 0; i < code_paths_length; i++) {
@@ -581,7 +582,22 @@
std::string profile_file_str(raw_profile_file);
env->ReleaseStringUTFChars(profile_file, raw_profile_file);
- Runtime::Current()->RegisterAppInfo(code_paths_vec, profile_file_str);
+ std::string foreign_dex_profile_path_str = "";
+ if (foreign_dex_profile_path != nullptr) {
+ const char* raw_foreign_dex_profile_path =
+ env->GetStringUTFChars(foreign_dex_profile_path, nullptr);
+ foreign_dex_profile_path_str.assign(raw_foreign_dex_profile_path);
+ env->ReleaseStringUTFChars(foreign_dex_profile_path, raw_foreign_dex_profile_path);
+ }
+
+ const char* raw_app_dir = env->GetStringUTFChars(app_dir, nullptr);
+ std::string app_dir_str(raw_app_dir);
+ env->ReleaseStringUTFChars(app_dir, raw_app_dir);
+
+ Runtime::Current()->RegisterAppInfo(code_paths_vec,
+ profile_file_str,
+ foreign_dex_profile_path_str,
+ app_dir_str);
}
static jboolean VMRuntime_isBootClassPathOnDisk(JNIEnv* env, jclass, jstring java_instruction_set) {
@@ -638,7 +654,7 @@
NATIVE_METHOD(VMRuntime, isCheckJniEnabled, "!()Z"),
NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"),
NATIVE_METHOD(VMRuntime, registerAppInfo,
- "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V"),
+ "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V"),
NATIVE_METHOD(VMRuntime, isBootClassPathOnDisk, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"),
};
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index ea26d58..3e6d0b5 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -442,6 +442,10 @@
+ std::string(dex_location));
}
}
+
+ // TODO(calin): Consider optimizing this knowing that is useless to record the
+ // use of fully compiled apks.
+ Runtime::Current()->NotifyDexLoaded(dex_location);
return dex_files;
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 47ef214..c2d2dca 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -120,6 +120,7 @@
#include "os.h"
#include "parsed_options.h"
#include "profiler.h"
+#include "jit/profile_saver.h"
#include "quick/quick_method_frame_info.h"
#include "reflection.h"
#include "runtime_options.h"
@@ -1717,7 +1718,9 @@
}
void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths,
- const std::string& profile_output_filename) {
+ const std::string& profile_output_filename,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir) {
if (jit_.get() == nullptr) {
// We are not JITing. Nothing to do.
return;
@@ -1740,7 +1743,18 @@
}
profile_output_filename_ = profile_output_filename;
- jit_->StartProfileSaver(profile_output_filename, code_paths);
+ jit_->StartProfileSaver(profile_output_filename,
+ code_paths,
+ foreign_dex_profile_path,
+ app_dir);
+}
+
+void Runtime::NotifyDexLoaded(const std::string& dex_location) {
+ VLOG(profiler) << "Notify dex loaded: " << dex_location;
+ // We know that if the ProfileSaver is started then we can record profile information.
+ if (ProfileSaver::IsStarted()) {
+ ProfileSaver::NotifyDexUse(dex_location);
+ }
}
// Transaction support.
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 83e77d2..8e99f80 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -467,7 +467,10 @@
}
void RegisterAppInfo(const std::vector<std::string>& code_paths,
- const std::string& profile_output_filename);
+ const std::string& profile_output_filename,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir);
+ void NotifyDexLoaded(const std::string& dex_location);
// Transaction support.
bool IsActiveTransaction() const {
diff --git a/test/577-profile-foreign-dex/expected.txt b/test/577-profile-foreign-dex/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/577-profile-foreign-dex/expected.txt
diff --git a/test/577-profile-foreign-dex/info.txt b/test/577-profile-foreign-dex/info.txt
new file mode 100644
index 0000000..090db3f
--- /dev/null
+++ b/test/577-profile-foreign-dex/info.txt
@@ -0,0 +1 @@
+Check that we record the use of foreign dex files when profiles are enabled.
diff --git a/test/577-profile-foreign-dex/run b/test/577-profile-foreign-dex/run
new file mode 100644
index 0000000..ad57d14
--- /dev/null
+++ b/test/577-profile-foreign-dex/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+exec ${RUN} \
+ --runtime-option -Xjitsaveprofilinginfo \
+ --runtime-option -Xusejit:true \
+ "${@}"
diff --git a/test/577-profile-foreign-dex/src-ex/OtherDex.java b/test/577-profile-foreign-dex/src-ex/OtherDex.java
new file mode 100644
index 0000000..cba73b3
--- /dev/null
+++ b/test/577-profile-foreign-dex/src-ex/OtherDex.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+public class OtherDex {
+}
diff --git a/test/577-profile-foreign-dex/src/Main.java b/test/577-profile-foreign-dex/src/Main.java
new file mode 100644
index 0000000..0cd85b5
--- /dev/null
+++ b/test/577-profile-foreign-dex/src/Main.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+public class Main {
+
+ private static final String PROFILE_NAME = "primary.prof";
+ private static final String APP_DIR_PREFIX = "app_dir_";
+ private static final String FOREIGN_DEX_PROFILE_DIR = "foreign-dex";
+ private static final String TEMP_FILE_NAME_PREFIX = "dummy";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+
+ public static void main(String[] args) throws Exception {
+ File tmpFile = null;
+ File appDir = null;
+ File profileFile = null;
+ File foreignDexProfileDir = null;
+
+ try {
+ // Create the necessary files layout.
+ tmpFile = createTempFile();
+ appDir = new File(tmpFile.getParent(), APP_DIR_PREFIX + tmpFile.getName());
+ appDir.mkdir();
+ foreignDexProfileDir = new File(tmpFile.getParent(), FOREIGN_DEX_PROFILE_DIR);
+ foreignDexProfileDir.mkdir();
+ profileFile = createTempFile();
+
+ String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar";
+
+ // Register the app with the runtime
+ VMRuntime.registerAppInfo(profileFile.getPath(), appDir.getPath(),
+ new String[] { codePath }, foreignDexProfileDir.getPath());
+
+ testMarkerForForeignDex(foreignDexProfileDir);
+ testMarkerForCodePath(foreignDexProfileDir);
+ testMarkerForApplicationDexFile(foreignDexProfileDir, appDir);
+ } finally {
+ if (tmpFile != null) {
+ tmpFile.delete();
+ }
+ if (profileFile != null) {
+ profileFile.delete();
+ }
+ if (foreignDexProfileDir != null) {
+ foreignDexProfileDir.delete();
+ }
+ if (appDir != null) {
+ appDir.delete();
+ }
+ }
+ }
+
+ // Verify we actually create a marker on disk for foreign dex files.
+ private static void testMarkerForForeignDex(File foreignDexProfileDir) throws Exception {
+ String foreignDex = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar";
+ loadDexFile(foreignDex);
+ checkMarker(foreignDexProfileDir, foreignDex, /* exists */ true);
+ }
+
+ // Verify we do not create a marker on disk for dex files path of the code path.
+ private static void testMarkerForCodePath(File foreignDexProfileDir) throws Exception {
+ String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar";
+ loadDexFile(codePath);
+ checkMarker(foreignDexProfileDir, codePath, /* exists */ false);
+ }
+
+ private static void testMarkerForApplicationDexFile(File foreignDexProfileDir, File appDir)
+ throws Exception {
+ // Copy the -ex jar to the application directory and load it from there.
+ // This will record duplicate class conflicts but we don't care for this use case.
+ File foreignDex = new File(System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar");
+ File appDex = new File(appDir, "appDex.jar");
+ try {
+ copyFile(foreignDex, appDex);
+
+ loadDexFile(appDex.getAbsolutePath());
+ checkMarker(foreignDexProfileDir, appDex.getAbsolutePath(), /* exists */ false);
+ } finally {
+ if (appDex != null) {
+ appDex.delete();
+ }
+ }
+ }
+
+ private static void checkMarker(File foreignDexProfileDir, String dexFile, boolean exists) {
+ File marker = new File(foreignDexProfileDir, dexFile.replace('/', '@'));
+ boolean result_ok = exists ? marker.exists() : !marker.exists();
+ if (!result_ok) {
+ throw new RuntimeException("Marker test failed for:" + marker.getPath());
+ }
+ }
+
+ private static void loadDexFile(String dexFile) throws Exception {
+ Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+ if (pathClassLoader == null) {
+ throw new RuntimeException("Couldn't find path class loader class");
+ }
+ Constructor constructor =
+ pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
+ constructor.newInstance(
+ dexFile, ClassLoader.getSystemClassLoader());
+ }
+
+ private static class VMRuntime {
+ private static final Method registerAppInfoMethod;
+ static {
+ try {
+ Class c = Class.forName("dalvik.system.VMRuntime");
+ registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo",
+ String.class, String.class, String[].class, String.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void registerAppInfo(String pkgName, String appDir,
+ String[] codePath, String foreignDexProfileDir) throws Exception {
+ registerAppInfoMethod.invoke(null, pkgName, appDir, codePath, foreignDexProfileDir);
+ }
+ }
+
+ private static void copyFile(File fromFile, File toFile) throws Exception {
+ FileInputStream in = new FileInputStream(fromFile);
+ FileOutputStream out = new FileOutputStream(toFile);
+ try {
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = in.read(buffer)) >= 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ } finally {
+ out.flush();
+ try {
+ out.getFD().sync();
+ } catch (IOException e) {
+ }
+ out.close();
+ in.close();
+ }
+ }
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+}