public static final int INSTALL_SUCCEEDED = 1;
/**
+ * Native Library Copy return code: this is passed to {@link PackageManagerService} by
+ * {@link NativeLibararyHelper} on successful copy of native library.
+ * It indicates that the native library being copied matches 2nd ABI.
+ * @hide
+ */
+
+ public static final int INSTALL_ABI2_SUCCEEDED = 2;
+
+ /**
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package is
* already installed.
package com.android.internal.content;
+import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.SystemProperties;
import android.util.Slog;
import java.io.File;
public static long sumNativeBinariesLI(File apkFile) {
final String cpuAbi = Build.CPU_ABI;
final String cpuAbi2 = Build.CPU_ABI2;
- return nativeSumNativeBinaries(apkFile.getPath(), cpuAbi, cpuAbi2);
+
+ String abi2 = SystemProperties.get("ro.product.cpu.abi2");
+ if (abi2.length() == 0) {
+ return nativeSumNativeBinaries(apkFile.getPath(), cpuAbi, cpuAbi2);
+ } else {
+ // abi2 is set, houdini is enabled
+ long result = nativeSumNativeBinaries(apkFile.getPath(), cpuAbi, cpuAbi2);
+ if (result == 0) {
+ String abiUpgrade = SystemProperties.get("ro.product.cpu.upgradeabi", "armeabi");
+ result = nativeSumNativeBinaries(apkFile.getPath(), cpuAbi, abiUpgrade);
+ }
+ return result;
+ }
}
+ private static native int nativeListNativeBinaries(String file, String cpuAbi, String cpuAbi2);
+
+ /**
+ * List the native binaries info in an APK.
+ *
+ * @param apkFile APK file to scan for native libraries
+ * @return {@link PackageManager#INSTALL_SUCCEEDED} or {@link PackageManager#INSTALL_ABI2_SUCCEEDED}
+ * or another error code from that class if not
+ */
+ public static int listNativeBinariesLI(File apkFile) {
+ final String cpuAbi = Build.CPU_ABI;
+ final String cpuAbi2 = Build.CPU_ABI2;
+
+ String abi2 = SystemProperties.get("ro.product.cpu.abi2");
+ if (abi2.length() == 0) {
+ return nativeListNativeBinaries(apkFile.getPath(), cpuAbi, cpuAbi2);
+ } else {
+ // abi2 is set, houdini is enabled
+ int result = nativeListNativeBinaries(apkFile.getPath(), cpuAbi, cpuAbi2);
+ if ((result != PackageManager.INSTALL_SUCCEEDED) && (result != PackageManager.INSTALL_ABI2_SUCCEEDED)) {
+ String abiUpgrade = SystemProperties.get("ro.product.cpu.upgradeabi", "armeabi");
+ result = nativeListNativeBinaries(apkFile.getPath(), cpuAbi, abiUpgrade);
+ }
+ return result;
+ }
+ }
+
+
private native static int nativeCopyNativeBinaries(String filePath, String sharedLibraryPath,
String cpuAbi, String cpuAbi2);
*
* @param apkFile APK file to scan for native libraries
* @param sharedLibraryDir directory for libraries to be copied to
- * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
- * error code from that class if not
+ * @return {@link PackageManager#INSTALL_SUCCEEDED} or {@link PackageManager#INSTALL_ABI2_SUCCEEDED}
+ * if successful or another error code from that class if not
*/
public static int copyNativeBinariesIfNeededLI(File apkFile, File sharedLibraryDir) {
final String cpuAbi = Build.CPU_ABI;
final String cpuAbi2 = Build.CPU_ABI2;
- return nativeCopyNativeBinaries(apkFile.getPath(), sharedLibraryDir.getPath(), cpuAbi,
- cpuAbi2);
+
+ String abi2 = SystemProperties.get("ro.product.cpu.abi2");
+ if (abi2.length() == 0) {
+ return nativeCopyNativeBinaries(apkFile.getPath(), sharedLibraryDir.getPath(), cpuAbi, cpuAbi2);
+ } else {
+ // abi2 is set, houdini is enabled
+ int result = nativeCopyNativeBinaries(apkFile.getPath(), sharedLibraryDir.getPath(), cpuAbi, cpuAbi2);
+ if ((result != PackageManager.INSTALL_SUCCEEDED) && (result != PackageManager.INSTALL_ABI2_SUCCEEDED)) {
+ String abiUpgrade = SystemProperties.get("ro.product.cpu.upgradeabi", "armeabi");
+ result = nativeCopyNativeBinaries(apkFile.getPath(), sharedLibraryDir.getPath(), cpuAbi, abiUpgrade);
+ }
+ return result;
+ }
}
// Convenience method to call removeNativeBinariesFromDirLI(File)
LOCAL_CFLAGS += -DPACKED=""
endif
+ifeq ($(INTEL_HOUDINI), true)
+ LOCAL_CFLAGS += -DWITH_HOUDINI
+ LOCAL_STATIC_LIBRARIES += libhoudini_hook
+endif
+
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER
endif
#define LOG_TRACE(...)
//#define LOG_TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#ifdef WITH_HOUDINI
+namespace houdini {
+void* hookDlopen(const char* filename, int flag, bool* useHoudini);
+void* hookDlsym(bool useHoudini, void* handle, const char* symbol);
+void hookCreateActivity(bool useHoudini, void* createActivityFunc, void* activity, void*houdiniActivity, void* savedState, size_t savedStateSize);
+}
+#endif
+
namespace android
{
createActivityFunc = _createFunc;
nativeWindow = NULL;
mainWorkRead = mainWorkWrite = -1;
+#ifdef WITH_HOUDINI
+ houdiniNativeActivity = NULL;
+#endif
}
~NativeCode() {
// is really no benefit to unloading the code.
//dlclose(dlhandle);
}
+#ifdef WITH_HOUDINI
+ if (houdiniNativeActivity != NULL)
+ delete houdiniNativeActivity;
+#endif
}
void setSurface(jobject _surface) {
int mainWorkRead;
int mainWorkWrite;
sp<MessageQueue> messageQueue;
+#ifdef WITH_HOUDINI
+ ANativeActivity *houdiniNativeActivity;
+#endif
};
void android_NativeActivity_finish(ANativeActivity* activity) {
const char* pathStr = env->GetStringUTFChars(path, NULL);
NativeCode* code = NULL;
+#ifdef WITH_HOUDINI
+ bool useHoudini = false;
+ void* handle = houdini::hookDlopen(pathStr, RTLD_LAZY, &useHoudini);
+#else
void* handle = dlopen(pathStr, RTLD_LAZY);
+#endif
env->ReleaseStringUTFChars(path, pathStr);
if (handle != NULL) {
const char* funcStr = env->GetStringUTFChars(funcName, NULL);
+#ifdef WITH_HOUDINI
+ code = new NativeCode(handle, (ANativeActivity_createFunc*)
+ houdini::hookDlsym(useHoudini, handle, funcStr));
+#else
code = new NativeCode(handle, (ANativeActivity_createFunc*)
dlsym(handle, funcStr));
+#endif
env->ReleaseStringUTFChars(funcName, funcStr);
if (code->createActivityFunc == NULL) {
rawSavedSize = env->GetArrayLength(savedState);
}
+#ifdef WITH_HOUDINI
+ if (useHoudini) {
+ /*
+ * If houdini is used, code is used by x86 code. So we create
+ * a houdini version for code. x86 version will store peer's
+ * pointer in houdiniNativeActivity each other.
+ */
+ code->houdiniNativeActivity = new ANativeActivity;
+ *code->houdiniNativeActivity = *(ANativeActivity *)code;
+ }
+
+ houdini::hookCreateActivity(useHoudini, (void*)code->createActivityFunc, (void*)code, (void*)code->houdiniNativeActivity, (void*)rawSavedState, rawSavedSize);
+#else
code->createActivityFunc(code, rawSavedState, rawSavedSize);
+#endif
if (rawSavedState != NULL) {
env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
// These match PackageManager.java install codes
typedef enum {
INSTALL_SUCCEEDED = 1,
+#ifdef WITH_HOUDINI
+ INSTALL_ABI2_SUCCEEDED = 2,
+#endif
INSTALL_FAILED_INVALID_APK = -2,
INSTALL_FAILED_INSUFFICIENT_STORAGE = -4,
+#ifdef WITH_HOUDINI
+ INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16,
+#endif
INSTALL_FAILED_CONTAINER_ERROR = -18,
INSTALL_FAILED_INTERNAL_ERROR = -110,
} install_status_t;
return INSTALL_SUCCEEDED;
}
+#ifdef WITH_HOUDINI
+static install_status_t
+listFiles(JNIEnv* env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
+{
+ return INSTALL_SUCCEEDED;
+}
+#endif
+
/*
* Copy the native library if needed.
*
char fileName[PATH_MAX];
bool hasPrimaryAbi = false;
+#ifdef WITH_HOUDINI
+ bool useSecondaryAbi = false;
+ bool noMatchAbi = false;
+#endif
for (int i = 0; i < N; i++) {
const ZipEntryRO entry = zipFile.findEntryByIndex(i);
ALOGV("Already saw primary ABI, skipping secondary ABI %s\n", cpuAbi2.c_str());
continue;
} else {
+#ifdef WITH_HOUDINI
+ useSecondaryAbi = true;
+#endif
ALOGV("Using secondary ABI %s\n", cpuAbi2.c_str());
}
} else {
+#ifdef WITH_HOUDINI
+ noMatchAbi = true;
+#endif
ALOGV("abi didn't match anything: %s (end at %zd)\n", cpuAbiOffset, cpuAbiRegionSize);
continue;
}
}
}
+#ifdef WITH_HOUDINI
+ if (!hasPrimaryAbi && !useSecondaryAbi && noMatchAbi)
+ return INSTALL_FAILED_CPU_ABI_INCOMPATIBLE;
+
+ if (!hasPrimaryAbi && useSecondaryAbi)
+ return INSTALL_ABI2_SUCCEEDED;
+#endif
+
return INSTALL_SUCCEEDED;
}
return totalSize;
}
+#ifdef WITH_HOUDINI
+static jint
+com_android_internal_content_NativeLibraryHelper_listNativeBinaries(JNIEnv *env, jclass clazz,
+ jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2)
+{
+ return (jint) iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2, listFiles, NULL);
+}
+#endif
+
static JNINativeMethod gMethods[] = {
{"nativeCopyNativeBinaries",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
{"nativeSumNativeBinaries",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
(void *)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
+#ifdef WITH_HOUDINI
+ {"nativeListNativeBinaries",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void *)com_android_internal_content_NativeLibraryHelper_listNativeBinaries},
+#endif
};
final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME);
if (sharedLibraryDir.mkdir()) {
int ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(codeFile, sharedLibraryDir);
- if (ret != PackageManager.INSTALL_SUCCEEDED) {
+ if ((ret != PackageManager.INSTALL_SUCCEEDED) && (ret != PackageManager.INSTALL_ABI2_SUCCEEDED)) {
Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath());
PackageHelper.destroySdDir(newCid);
return null;
--- /dev/null
+package com.android.server.pm;
+/*
+ * Copyright (C) 2006 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.util.List;
+import java.util.ArrayList;
+
+/*
+ * class CheckExt is trying to check params
+ */
+public class CheckExt implements ICheckExt {
+ final private String TAG = "CheckExt";
+ private List<ICheckExt> checklist;
+
+ public CheckExt() {
+ checklist = new ArrayList<ICheckExt>();
+ ICheckExt check = new xmlCheckExt();
+ checklist.add(check);
+ }
+
+ public boolean doCheck(String... params) {
+ if (checklist.size() == 0)
+ return false;
+ ICheckExt check;
+ for (int i = 0; i < checklist.size(); i++) {
+ check = checklist.get(i);
+ if(check.doCheck(params))
+ return true;
+
+ }
+ return false;
+ }
+}
--- /dev/null
+package com.android.server.pm;
+/*
+ * Copyright (C) 2006 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 interface ICheckExt {
+
+ /*Function: doCheck
+ *Description:
+ * check parameters to decide whether it should pass or not
+ *Parameter:
+ * params : parameters for apk to check
+ *Return:
+ * true - check pass
+ * false - check fail
+ */
+ boolean doCheck(String... params);
+}
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
+import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
// or internal storage.
private IMediaContainerService mContainerService = null;
+ // Packages that have been installed with library matching 2nd ABI.
+ final HashMap<Integer, String> mPackagesMatchABI2 = new HashMap<Integer,String>();
+
static final int SEND_PENDING_BROADCAST = 1;
static final int MCS_BOUND = 3;
static final int END_COPY = 4;
return new File(mUserAppDataDir.getAbsolutePath() + File.separator + userId);
}
+ private void writeAppwithABI2() {
+ File outputFile;
+ FileOutputStream out = null;
+ File appDataDir = new File("/data/data");
+
+ try {
+ File tempFile = File.createTempFile("tmp", "tmp", appDataDir);
+ String tempFilePath = tempFile.getPath();
+ outputFile = new File("/data/data/.appwithABI2");
+ if (FileUtils.setPermissions(tempFilePath,
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR |
+ FileUtils.S_IRGRP | FileUtils.S_IROTH, -1, -1) != 0
+ || !tempFile.renameTo(outputFile)) {
+ tempFile.delete();
+ }
+ out = new FileOutputStream(outputFile);
+ Iterator<HashMap.Entry<Integer, String>>
+ it = mPackagesMatchABI2.entrySet().iterator();
+ while (it.hasNext()) {
+ HashMap.Entry<Integer, String> ent = it.next();
+ int userID = ent.getKey().intValue();
+ out.write(userID & 0xff);
+ out.write((userID>>8) & 0xff);
+ out.write((userID>>16) & 0xff);
+ out.write((userID>>24) & 0xff);
+ Slog.i(TAG, "Data written:"+ userID);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "File Access Error: Not Able to write Data into /data/data/.appwithABI2");
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ Slog.i(TAG, "Data written into /data/data/.appwithABI2");
+ }
+ } catch (IOException e) {}
+ }
+ }
+
private File getDataPathForPackage(String packageName, int userId) {
/*
* Until we fully support multiple users, return the directory we
Slog.w(TAG, "Package " + pkg.packageName
+ " was transferred to another, but its .apk remains");
}
+
+ String abi2 = SystemProperties.get("ro.product.cpu.abi2");
+ if (abi2.length() != 0) {
+ // abi2 is set, houdini is enabled
+ PackageSetting p = mSettings.mPackages.get(pkg.packageName);
+ if ((p != null) && (!p.codePath.equals(destCodeFile))){
+
+ // Already existing package. Make sure not upgrade to black list
+ int result = NativeLibraryHelper.listNativeBinariesLI(scanFile);
+
+ if (result == PackageManager.INSTALL_ABI2_SUCCEEDED) {
+ ICheckExt check = new CheckExt();
+ if(check.doCheck(pkg.packageName)){
+ Slog.i(TAG, "Reject application in black list::" + pkg.packageName);
+ mLastScanError = PackageManager.INSTALL_FAILED_INVALID_APK;
+ return null;
+ }
+ }
+
+ }
+ }
// Just create the setting, don't add it yet. For already existing packages
// the PkgSetting exists already and doesn't have to be created.
}
try {
- if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) {
+ int copyRet = copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir);
+ Integer pkgUidInt = new Integer(pkg.applicationInfo.uid);
+ if (copyRet == PackageManager.INSTALL_SUCCEEDED) {
+ String abi2 = SystemProperties.get("ro.product.cpu.abi2");
+ if (abi2.length() != 0 && mPackagesMatchABI2.containsKey(pkgUidInt)) {
+ Slog.i(TAG, "Replace package with primary ABI Library");
+ mPackagesMatchABI2.remove(pkgUidInt);
+ writeAppwithABI2();
+ }
+ } else if (copyRet == PackageManager.INSTALL_ABI2_SUCCEEDED) {
+ ICheckExt check = new CheckExt();
+ if(check.doCheck(pkgName)) {
+ Slog.i(TAG, "Package with second ABI is in black list: " + pkgUidInt + pkg.applicationInfo.processName);
+ mLastScanError = PackageManager.INSTALL_FAILED_INVALID_APK;
+ return null;
+ }
+ Slog.i(TAG, "Package installed with second ABI Library: " + pkgUidInt + pkg.applicationInfo.processName);
+ mPackagesMatchABI2.put(pkgUidInt, pkg.applicationInfo.processName);
+ writeAppwithABI2();
+ } else {
Slog.e(TAG, "Unable to copy native libraries");
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
return null;
final PackageParser.Package pkg = ps.pkg;
if (pkg != null) {
cleanPackageDataStructuresLILPw(pkg, chatty);
+
+ String abi2 = SystemProperties.get("ro.product.cpu.abi2");
+ if (abi2.length() != 0 && mPackagesMatchABI2.containsKey(new Integer(pkg.applicationInfo.uid))) {
+ Slog.i(TAG, "Uninstall package with second ABI Library");
+ mPackagesMatchABI2.remove(new Integer(pkg.applicationInfo.uid));
+ writeAppwithABI2();
+ }
+
}
}
}
mAppDirs.remove(pkg.mPath);
}
cleanPackageDataStructuresLILPw(pkg, chatty);
+
+ String abi2 = SystemProperties.get("ro.product.cpu.abi2");
+ if (abi2.length() != 0 && mPackagesMatchABI2.containsKey(new Integer(pkg.applicationInfo.uid))) {
+ Slog.i(TAG, "Uninstall package with second ABI Library");
+ mPackagesMatchABI2.remove(new Integer(pkg.applicationInfo.uid));
+ writeAppwithABI2();
+ }
+
}
}
}
try {
int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile);
- if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+ if (copyRet != PackageManager.INSTALL_SUCCEEDED && copyRet != PackageManager.INSTALL_ABI2_SUCCEEDED) {
return copyRet;
}
} catch (IOException e) {
--- /dev/null
+package com.android.server.pm;
+/*
+ * Copyright (C) 2006 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.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.Activity;
+import android.content.res.AssetManager;
+import android.util.Log;
+import android.util.Xml;
+
+/*
+ * class xmlCheckExt is trying to check by xml rules definition
+ */
+public class xmlCheckExt implements ICheckExt {
+ final private String TAG = "xmlCheckExt";
+ final private String CHECKXMLPATH = "/system/lib/arm/check.xml";
+ private HashMap<String,String > mMap = new HashMap<String,String >();
+
+ public boolean doCheck(String... params) {
+ String param = null;
+ try {
+ int eventType;
+ String tag;
+ if (params.length == 0)
+ return false;
+ param = params[0];
+ XmlPullParser xmlParser = Xml.newPullParser();
+ File file = new File(CHECKXMLPATH);
+ if (!file.exists())
+ return false;
+ InputStream in=null;
+ in = new FileInputStream(file);
+ xmlParser.setInput(in, "utf-8");
+
+ eventType = xmlParser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ tag = xmlParser.getName();
+ Log.d(TAG,"<"+tag+">");
+ addTag(tag, xmlParser.nextText());
+ break;
+ case XmlPullParser.END_TAG:
+ tag = xmlParser.getName();
+ Log.d(TAG,"</"+tag+">");
+ break;
+ default:
+ break;
+ }
+ eventType = xmlParser.next();
+ }
+ in.close();
+ } catch (XmlPullParserException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return checkPkgName(param);
+ }
+
+ /*Function:checkTag
+ *Description:
+ * add tag name to hash map
+ *Parameter:
+ * tag - tag name in xml
+ * text - text for the tag
+ *Return:
+ * true
+ */
+ boolean addTag(String tag, String text) {
+ String pkgName = text;
+ Log.d(TAG, " pkgName = " + pkgName);
+ if(!mMap.containsKey(pkgName))
+ mMap.put(pkgName,tag);
+ return true;
+ }
+
+ boolean checkPkgName(String pkgName) {
+ return mMap.containsKey(pkgName);
+ }
+
+}