diff options
123 files changed, 3909 insertions, 1252 deletions
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk index 7c25354..74a2f7b 100644 --- a/cmds/app_process/Android.mk +++ b/cmds/app_process/Android.mk @@ -1,6 +1,5 @@ LOCAL_PATH:= $(call my-dir) -# 32-bit app_process include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ @@ -15,10 +14,14 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= app_process LOCAL_MULTILIB := both -LOCAL_MODULE_STEM_32 := app_process +LOCAL_MODULE_STEM_32 := app_process32 LOCAL_MODULE_STEM_64 := app_process64 include $(BUILD_EXECUTABLE) +# Create a symlink from app_process to app_process32 or 64 +# depending on the target configuration. +include $(BUILD_SYSTEM)/executable_prefer_symlink.mk + # Build a variant of app_process binary linked with ASan runtime. # ARM-only at the moment. ifeq ($(TARGET_ARCH),arm) diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 8d2b739..74ccbc2 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -10,14 +10,17 @@ #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <utils/Log.h> -#include <cutils/process_name.h> #include <cutils/memory.h> +#include <cutils/process_name.h> +#include <cutils/properties.h> #include <cutils/trace.h> #include <android_runtime/AndroidRuntime.h> +#include <private/android_filesystem_config.h> // for AID_SYSTEM #include <stdlib.h> #include <stdio.h> #include <unistd.h> +#include <sys/prctl.h> namespace android { @@ -30,31 +33,22 @@ void app_usage() class AppRuntime : public AndroidRuntime { public: - AppRuntime() - : mParentDir(NULL) - , mClassName(NULL) + AppRuntime(char* argBlockStart, const size_t argBlockLength) + : AndroidRuntime(argBlockStart, argBlockLength) , mClass(NULL) - , mArgC(0) - , mArgV(NULL) { } -#if 0 - // this appears to be unused - const char* getParentDir() const - { - return mParentDir; - } -#endif - - const char* getClassName() const - { - return mClassName; + void setClassNameAndArgs(const String8& className, int argc, char * const *argv) { + mClassName = className; + for (int i = 0; i < argc; ++i) { + mArgs.add(String8(argv[i])); + } } virtual void onVmCreated(JNIEnv* env) { - if (mClassName == NULL) { + if (mClassName.isEmpty()) { return; // Zygote. Nothing to do here. } @@ -71,10 +65,10 @@ public: * executing boot class Java code and thereby deny ourselves access to * non-boot classes. */ - char* slashClassName = toSlashClassName(mClassName); + char* slashClassName = toSlashClassName(mClassName.string()); mClass = env->FindClass(slashClassName); if (mClass == NULL) { - ALOGE("ERROR: could not find class '%s'\n", mClassName); + ALOGE("ERROR: could not find class '%s'\n", mClassName.string()); } free(slashClassName); @@ -88,7 +82,7 @@ public: proc->startThreadPool(); AndroidRuntime* ar = AndroidRuntime::getRuntime(); - ar->callMain(mClassName, mClass, mArgC, mArgV); + ar->callMain(mClassName, mClass, mArgs); IPCThreadState::self()->stopProcess(); } @@ -105,7 +99,7 @@ public: virtual void onExit(int code) { - if (mClassName == NULL) { + if (mClassName.isEmpty()) { // if zygote IPCThreadState::self()->stopProcess(); } @@ -114,46 +108,119 @@ public: } - const char* mParentDir; - const char* mClassName; + String8 mClassName; + Vector<String8> mArgs; jclass mClass; - int mArgC; - const char* const* mArgV; }; } using namespace android; -/* - * sets argv0 to as much of newArgv0 as will fit - */ -static void setArgv0(const char *argv0, const char *newArgv0) -{ - strlcpy(const_cast<char *>(argv0), newArgv0, strlen(argv0)); +static size_t computeArgBlockSize(int argc, char* const argv[]) { + // TODO: This assumes that all arguments are allocated in + // contiguous memory. There isn't any documented guarantee + // that this is the case, but this is how the kernel does it + // (see fs/exec.c). + // + // Also note that this is a constant for "normal" android apps. + // Since they're forked from zygote, the size of their command line + // is the size of the zygote command line. + // + // We change the process name of the process by over-writing + // the start of the argument block (argv[0]) with the new name of + // the process, so we'd mysteriously start getting truncated process + // names if the zygote command line decreases in size. + uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]); + uintptr_t end = reinterpret_cast<uintptr_t>(argv[argc - 1]); + end += strlen(argv[argc - 1]); + + return (end - start); +} + +static void maybeCreateDalvikCache() { +#if defined(__aarch64__) + static const char kInstructionSet[] = "arm64"; +#elif defined(__x86_64__) + static const char kInstructionSet[] = "x86_64"; +#elif defined(__arm__) + static const char kInstructionSet[] = "arm"; +#elif defined(__i386__) + static const char kInstructionSet[] = "x86"; +#elif defined (__mips__) + static const char kInstructionSet[] = "mips"; +#else +#error "Unknown instruction set" +#endif + const char* androidRoot = getenv("ANDROID_DATA"); + LOG_ALWAYS_FATAL_IF(androidRoot == NULL, "ANDROID_DATA environment variable unset"); + + char dalvikCacheDir[PATH_MAX]; + const int numChars = snprintf(dalvikCacheDir, PATH_MAX, + "%s/dalvik-cache/%s", androidRoot, kInstructionSet); + LOG_ALWAYS_FATAL_IF((numChars >= PATH_MAX || numChars < 0), + "Error constructing dalvik cache : %s", strerror(errno)); + + int result = mkdir(dalvikCacheDir, 0771); + LOG_ALWAYS_FATAL_IF((result < 0 && errno != EEXIST), + "Error creating cache dir %s : %s", dalvikCacheDir, strerror(errno)); + + // We always perform these steps because the directory might + // already exist, with wider permissions and a different owner + // than we'd like. + result = chown(dalvikCacheDir, AID_SYSTEM, AID_SYSTEM); + LOG_ALWAYS_FATAL_IF((result < 0), "Error changing dalvik-cache ownership : %s", strerror(errno)); + + result = chmod(dalvikCacheDir, 0771); + LOG_ALWAYS_FATAL_IF((result < 0), + "Error changing dalvik-cache permissions : %s", strerror(errno)); } +#if defined(__LP64__) +static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64"; +static const char ZYGOTE_NICE_NAME[] = "zygote64"; +#else +static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32"; +static const char ZYGOTE_NICE_NAME[] = "zygote"; +#endif + int main(int argc, char* const argv[]) { - // These are global variables in ProcessState.cpp - mArgC = argc; - mArgV = argv; - - mArgLen = 0; - for (int i=0; i<argc; i++) { - mArgLen += strlen(argv[i]) + 1; + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return + // EINVAL. Don't die on such kernels. + if (errno != EINVAL) { + LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno)); + return 12; + } } - mArgLen--; - - AppRuntime runtime; - const char* argv0 = argv[0]; + AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); // Process command line arguments // ignore argv[0] argc--; argv++; - // Everything up to '--' or first non '-' arg goes to the vm + // Everything up to '--' or first non '-' arg goes to the vm. + // + // The first argument after the VM args is the "parent dir", which + // is currently unused. + // + // After the parent dir, we expect one or more the following internal + // arguments : + // + // --zygote : Start in zygote mode + // --start-system-server : Start the system server. + // --application : Start in application (stand alone, non zygote) mode. + // --nice-name : The nice name for this process. + // + // For non zygote starts, these arguments will be followed by + // the main class name. All remaining arguments are passed to + // the main method of this class. + // + // For zygote starts, all remaining arguments are passed to the zygote. + // main function. + int i = runtime.addVmArguments(argc, argv); @@ -161,45 +228,74 @@ int main(int argc, char* const argv[]) bool zygote = false; bool startSystemServer = false; bool application = false; - const char* parentDir = NULL; const char* niceName = NULL; - const char* className = NULL; + String8 className; + + ++i; // Skip unused "parent dir" argument. while (i < argc) { const char* arg = argv[i++]; - if (!parentDir) { - parentDir = arg; - } else if (strcmp(arg, "--zygote") == 0) { + if (strcmp(arg, "--zygote") == 0) { zygote = true; - niceName = "zygote"; + niceName = ZYGOTE_NICE_NAME; } else if (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; } else if (strcmp(arg, "--application") == 0) { application = true; } else if (strncmp(arg, "--nice-name=", 12) == 0) { niceName = arg + 12; + } else if (strncmp(arg, "--", 2) != 0) { + className.setTo(arg); + break; } else { - className = arg; + --i; break; } } + Vector<String8> args; + if (!className.isEmpty()) { + // We're not in zygote mode, the only argument we need to pass + // to RuntimeInit is the application argument. + // + // The Remainder of args get passed to startup class main(). Make + // copies of them before we overwrite them with the process name. + args.add(application ? String8("application") : String8("tool")); + runtime.setClassNameAndArgs(className, argc - i, argv + i); + } else { + // We're in zygote mode. + maybeCreateDalvikCache(); + + if (startSystemServer) { + args.add(String8("start-system-server")); + } + + char prop[PROP_VALUE_MAX]; + if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) { + LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.", + ABI_LIST_PROPERTY); + return 11; + } + + String8 abiFlag("--abi-list="); + abiFlag.append(prop); + args.add(abiFlag); + + // In zygote mode, pass all remaining arguments to the zygote + // main() method. + for (; i < argc; ++i) { + args.add(String8(argv[i])); + } + } + if (niceName && *niceName) { - setArgv0(argv0, niceName); + runtime.setArgv0(niceName); set_process_name(niceName); } - runtime.mParentDir = parentDir; - if (zygote) { - runtime.start("com.android.internal.os.ZygoteInit", - startSystemServer ? "start-system-server" : ""); + runtime.start("com.android.internal.os.ZygoteInit", args); } else if (className) { - // Remainder of args get passed to startup class main() - runtime.mClassName = className; - runtime.mArgC = argc - i; - runtime.mArgV = argv + i; - runtime.start("com.android.internal.os.RuntimeInit", - application ? "application" : "tool"); + runtime.start("com.android.internal.os.RuntimeInit", args); } else { fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 1a2ab81..7f7ae2c 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -156,12 +156,12 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, return NO_ERROR; } -status_t BootAnimation::initTexture(void* buffer, size_t len) +status_t BootAnimation::initTexture(const Animation::Frame& frame) { //StopWatch watch("blah"); SkBitmap bitmap; - SkMemoryStream stream(buffer, len); + SkMemoryStream stream(frame.map->getDataPtr(), frame.map->getDataLength()); SkImageDecoder* codec = SkImageDecoder::Factory(&stream); if (codec) { codec->setDitherImage(false); @@ -171,6 +171,11 @@ status_t BootAnimation::initTexture(void* buffer, size_t len) delete codec; } + // FileMap memory is never released until application exit. + // Release it now as the texture is already loaded and the memory used for + // the packed resource can be released. + frame.map->release(); + // ensure we can call getPixels(). No need to call unlock, since the // bitmap will go out of scope when we return from this method. bitmap.lockPixels(); @@ -406,6 +411,7 @@ bool BootAnimation::movie() String8 desString((char const*)descMap->getDataPtr(), descMap->getDataLength()); + descMap->release(); char const* s = desString.string(); Animation animation; @@ -530,9 +536,7 @@ bool BootAnimation::movie() glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - initTexture( - frame.map->getDataPtr(), - frame.map->getDataLength()); + initTexture(frame); } if (!clearReg.isEmpty()) { diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 22963c2..ba1c507 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -79,7 +79,7 @@ private: }; status_t initTexture(Texture* texture, AssetManager& asset, const char* name); - status_t initTexture(void* buffer, size_t len); + status_t initTexture(const Animation::Frame& frame); bool android(); bool movie(); diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java index a292ecb..a8896c2 100644 --- a/core/java/android/app/SharedPreferencesImpl.java +++ b/core/java/android/app/SharedPreferencesImpl.java @@ -19,6 +19,9 @@ package android.app; import android.content.SharedPreferences; import android.os.FileUtils; import android.os.Looper; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStat; import android.util.Log; import com.google.android.collect.Maps; @@ -44,10 +47,7 @@ import java.util.WeakHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; -import libcore.io.StructStat; final class SharedPreferencesImpl implements SharedPreferences { private static final String TAG = "SharedPreferencesImpl"; @@ -111,7 +111,7 @@ final class SharedPreferencesImpl implements SharedPreferences { Map map = null; StructStat stat = null; try { - stat = Libcore.os.stat(mFile.getPath()); + stat = Os.stat(mFile.getPath()); if (mFile.canRead()) { BufferedInputStream str = null; try { @@ -173,7 +173,7 @@ final class SharedPreferencesImpl implements SharedPreferences { * violation, but we explicitly want this one. */ BlockGuard.getThreadPolicy().onReadFromDisk(); - stat = Libcore.os.stat(mFile.getPath()); + stat = Os.stat(mFile.getPath()); } catch (ErrnoException e) { return true; } @@ -600,7 +600,7 @@ final class SharedPreferencesImpl implements SharedPreferences { str.close(); ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0); try { - final StructStat stat = Libcore.os.stat(mFile.getPath()); + final StructStat stat = Os.stat(mFile.getPath()); synchronized (this) { mStatTimestamp = stat.st_mtime; mStatSize = stat.st_size; diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index ced72f8..c2bbff0 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -668,6 +668,10 @@ public class WallpaperManager { * not "image/*" */ public Intent getCropAndSetWallpaperIntent(Uri imageUri) { + if (imageUri == null) { + throw new IllegalArgumentException("Image URI must not be null"); + } + if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme())) { throw new IllegalArgumentException("Image URI must be of the " + ContentResolver.SCHEME_CONTENT + " scheme type"); diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 67c772b..70a3797 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -29,6 +29,10 @@ import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; +import android.system.StructStat; import android.util.Log; import java.io.File; @@ -38,11 +42,6 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.concurrent.CountDownLatch; -import libcore.io.ErrnoException; -import libcore.io.Libcore; -import libcore.io.OsConstants; -import libcore.io.StructStat; - /** * Provides the central interface between an * application and Android's data backup infrastructure. An application that wishes @@ -191,7 +190,7 @@ public abstract class BackupAgent extends ContextWrapper { * the key supplied as part of the entity. Writing an entity with a negative * data size instructs the transport to delete whatever entity currently exists * under that key from the remote data set. - * + * * @param oldState An open, read-only ParcelFileDescriptor pointing to the * last backup state provided by the application. May be * <code>null</code>, in which case no prior state is being @@ -222,7 +221,7 @@ public abstract class BackupAgent extends ContextWrapper { * onRestore() throws an exception, the OS will assume that the * application's data may now be in an incoherent state, and will clear it * before proceeding. - * + * * @param data A structured wrapper around an open, read-only * file descriptor pointing to a full snapshot of the * application's data. The application should consume every @@ -412,7 +411,7 @@ public abstract class BackupAgent extends ContextWrapper { } // If it's a directory, enqueue its contents for scanning. - StructStat stat = Libcore.os.lstat(filePath); + StructStat stat = Os.lstat(filePath); if (OsConstants.S_ISLNK(stat.st_mode)) { if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file); continue; diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index cb0737e..477285d 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -20,6 +20,8 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.ParcelFileDescriptor; +import android.system.ErrnoException; +import android.system.Os; import android.util.Log; import java.io.File; @@ -27,9 +29,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import libcore.io.ErrnoException; -import libcore.io.Libcore; - /** * Global constant definitions et cetera related to the full-backup-to-fd * binary format. Nothing in this namespace is part of any API; it's all @@ -150,7 +149,7 @@ public class FullBackup { try { // explicitly prevent emplacement of files accessible by outside apps mode &= 0700; - Libcore.os.chmod(outFile.getPath(), (int)mode); + Os.chmod(outFile.getPath(), (int)mode); } catch (ErrnoException e) { e.rethrowAsIOException(); } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 9c46d96..a23cd7f 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -441,6 +441,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public String nativeLibraryDir; /** + * The ABI that this application requires, This is inferred from the ABIs + * of the native JNI libraries the application bundles. Will be {@code null} + * if this application does not require any particular ABI. + * + * {@hide} + */ + public String cpuAbi; + + /** * The kernel user-ID that has been assigned to this application; * currently this is not a unique ID (multiple applications can have * the same uid). @@ -570,6 +579,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { sourceDir = orig.sourceDir; publicSourceDir = orig.publicSourceDir; nativeLibraryDir = orig.nativeLibraryDir; + cpuAbi = orig.cpuAbi; resourceDirs = orig.resourceDirs; seinfo = orig.seinfo; sharedLibraryFiles = orig.sharedLibraryFiles; @@ -610,6 +620,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString(sourceDir); dest.writeString(publicSourceDir); dest.writeString(nativeLibraryDir); + dest.writeString(cpuAbi); dest.writeStringArray(resourceDirs); dest.writeString(seinfo); dest.writeStringArray(sharedLibraryFiles); @@ -649,6 +660,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { sourceDir = source.readString(); publicSourceDir = source.readString(); nativeLibraryDir = source.readString(); + cpuAbi = source.readString(); resourceDirs = source.readStringArray(); seinfo = source.readString(); sharedLibraryFiles = source.readStringArray(); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c97c2b8..8ce7e97 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -675,6 +675,25 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_USER_RESTRICTED = -111; /** + * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} + * if the system failed to install the package because its packaged native code did not + * match any of the ABIs supported by the system. + * + * @hide + */ + public static final int INSTALL_FAILED_NO_MATCHING_ABIS = -112; + + /** + * Internal return code for NativeLibraryHelper methods to indicate that the package + * being processed did not contain any native code. This is placed here only so that + * it can belong to the same value space as the other install failure codes. + * + * @hide + */ + public static final int NO_NATIVE_LIBRARIES = -113; + + /** * Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the * package's data directory. * diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 52564eb..c0963f5 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3533,10 +3533,13 @@ public class PackageParser { // For use by the package manager to keep track of the path to the // file an app came from. public String mScanPath; - - // For use by package manager to keep track of where it has done dexopt. - public boolean mDidDexOpt; - + + // For use by package manager to keep track of where it needs to do dexopt. + public boolean mDexOptNeeded = true; + + // For use by package manager to keep track of when a package was last used. + public long mLastPackageUsageTimeInMills; + // // User set enabled state. // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; // diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 197e3ff..a75372f 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -42,12 +42,8 @@ import android.util.LongSparseArray; public class CursorWindow extends SQLiteClosable implements Parcelable { private static final String STATS_TAG = "CursorWindowStats"; - /** The cursor window size. resource xml file specifies the value in kB. - * convert it to bytes here by multiplying with 1024. - */ - private static final int sCursorWindowSize = - Resources.getSystem().getInteger( - com.android.internal.R.integer.config_cursorWindowSize) * 1024; + // This static member will be evaluated when first used. + private static int sCursorWindowSize = -1; /** * The native CursorWindow object pointer. (FOR INTERNAL USE ONLY) @@ -100,6 +96,13 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { public CursorWindow(String name) { mStartPos = 0; mName = name != null && name.length() != 0 ? name : "<unnamed>"; + if (sCursorWindowSize < 0) { + /** The cursor window size. resource xml file specifies the value in kB. + * convert it to bytes here by multiplying with 1024. + */ + sCursorWindowSize = Resources.getSystem().getInteger( + com.android.internal.R.integer.config_cursorWindowSize) * 1024; + } mWindowPtr = nativeCreate(mName, sCursorWindowSize); if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window allocation of " + diff --git a/core/java/android/ddm/DdmHandleHeap.java b/core/java/android/ddm/DdmHandleHeap.java index cece556..e24aeb2 100644 --- a/core/java/android/ddm/DdmHandleHeap.java +++ b/core/java/android/ddm/DdmHandleHeap.java @@ -219,7 +219,7 @@ public class DdmHandleHeap extends ChunkHandler { if (false) Log.d("ddm-heap", "Heap GC request"); - System.gc(); + Runtime.getRuntime().gc(); return null; // empty response } diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index 22543e3..a725bec 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -24,13 +24,13 @@ import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.UnknownHostException; -import static libcore.io.OsConstants.IFA_F_DADFAILED; -import static libcore.io.OsConstants.IFA_F_DEPRECATED; -import static libcore.io.OsConstants.IFA_F_TENTATIVE; -import static libcore.io.OsConstants.RT_SCOPE_HOST; -import static libcore.io.OsConstants.RT_SCOPE_LINK; -import static libcore.io.OsConstants.RT_SCOPE_SITE; -import static libcore.io.OsConstants.RT_SCOPE_UNIVERSE; +import static android.system.OsConstants.IFA_F_DADFAILED; +import static android.system.OsConstants.IFA_F_DEPRECATED; +import static android.system.OsConstants.IFA_F_TENTATIVE; +import static android.system.OsConstants.RT_SCOPE_HOST; +import static android.system.OsConstants.RT_SCOPE_LINK; +import static android.system.OsConstants.RT_SCOPE_SITE; +import static android.system.OsConstants.RT_SCOPE_UNIVERSE; /** * Identifies an IP address on a network link. diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java index 119e533..643e8c2 100644 --- a/core/java/android/net/LocalSocketImpl.java +++ b/core/java/android/net/LocalSocketImpl.java @@ -22,9 +22,9 @@ import java.io.InputStream; import java.io.FileDescriptor; import java.net.SocketOptions; -import libcore.io.ErrnoException; -import libcore.io.Libcore; -import libcore.io.OsConstants; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; /** * Socket implementation used for android.net.LocalSocket and @@ -248,7 +248,7 @@ class LocalSocketImpl throw new IllegalStateException("unknown sockType"); } try { - fd = Libcore.os.socket(OsConstants.AF_UNIX, osType, 0); + fd = Os.socket(OsConstants.AF_UNIX, osType, 0); mFdCreatedInternally = true; } catch (ErrnoException e) { e.rethrowAsIOException(); @@ -268,7 +268,7 @@ class LocalSocketImpl return; } try { - Libcore.os.close(fd); + Os.close(fd); } catch (ErrnoException e) { e.rethrowAsIOException(); } diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java index 666832a..d730a7b 100644 --- a/core/java/android/net/http/X509TrustManagerExtensions.java +++ b/core/java/android/net/http/X509TrustManagerExtensions.java @@ -56,7 +56,8 @@ public class X509TrustManagerExtensions { if (tm instanceof TrustManagerImpl) { mDelegate = (TrustManagerImpl) tm; } else { - throw new IllegalArgumentException("tm is not a supported type of X509TrustManager"); + throw new IllegalArgumentException("tm is an instance of " + tm.getClass().getName() + + " which is not a supported type of X509TrustManager"); } } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index bc51a60..a61c4f3 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -74,7 +74,41 @@ public class Build { /** A hardware serial number, if available. Alphanumeric only, case-insensitive. */ public static final String SERIAL = getString("ro.serialno"); - + + /** + * An ordered list of ABIs supported by this device. The most preferred ABI is the first + * element in the list. + * + * See {@link #SUPPORTED_32_BIT_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}. + * + * @hide + */ + public static final String[] SUPPORTED_ABIS = SystemProperties.get("ro.product.cpu.abilist") + .split(","); + + /** + * An ordered list of <b>32 bit</b> ABIs supported by this device. The most preferred ABI + * is the first element in the list. + * + * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}. + * + * @hide + */ + public static final String[] SUPPORTED_32_BIT_ABIS = + SystemProperties.get("ro.product.cpu.abilist32").split(","); + + /** + * An ordered list of <b>64 bit</b> ABIs supported by this device. The most preferred ABI + * is the first element in the list. + * + * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_32_BIT_ABIS}. + * + * @hide + */ + public static final String[] SUPPORTED_64_BIT_ABIS = + SystemProperties.get("ro.product.cpu.abilist64").split(","); + + /** Various version strings. */ public static class VERSION { /** diff --git a/core/java/android/os/CommonClock.java b/core/java/android/os/CommonClock.java index 3a1da97..7f41c5d 100644 --- a/core/java/android/os/CommonClock.java +++ b/core/java/android/os/CommonClock.java @@ -20,7 +20,6 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetSocketAddress; import java.util.NoSuchElementException; -import static libcore.io.OsConstants.*; import android.content.ComponentName; import android.content.Context; @@ -32,6 +31,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; +import static android.system.OsConstants.*; /** * Used for accessing the android common time service's common clock and receiving notifications diff --git a/core/java/android/os/CommonTimeUtils.java b/core/java/android/os/CommonTimeUtils.java index 20755d9..ba060b8 100644 --- a/core/java/android/os/CommonTimeUtils.java +++ b/core/java/android/os/CommonTimeUtils.java @@ -20,7 +20,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetSocketAddress; import java.util.Locale; -import static libcore.io.OsConstants.*; +import static android.system.OsConstants.*; class CommonTimeUtils { /** diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index b5413db..7bf7367 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -42,6 +42,8 @@ public class Environment { private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE"; private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE"; private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT"; + private static final String ENV_OEM_ROOT = "OEM_ROOT"; + private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT"; /** {@hide} */ public static final String DIR_ANDROID = "Android"; @@ -56,6 +58,8 @@ public class Environment { public static final String DIRECTORY_ANDROID = DIR_ANDROID; private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system"); + private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem"); + private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor"); private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media"); private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull( @@ -244,6 +248,25 @@ public class Environment { } /** + * Return root directory of the "oem" partition holding OEM customizations, + * if any. If present, the partition is mounted read-only. + * + * @hide + */ + public static File getOemDirectory() { + return DIR_OEM_ROOT; + } + + /** + * Return root directory of the "vendor" partition that holds vendor-provided + * software that should persist across simple reflashing of the "system" partition. + * @hide + */ + public static File getVendorDirectory() { + return DIR_VENDOR_ROOT; + } + + /** * Gets the system directory available for secure storage. * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system). * Otherwise, it returns the unencrypted /data/system directory. diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index ff3e277..3e0b54a 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -16,13 +16,13 @@ package android.os; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; import android.util.Log; import android.util.Slog; -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; -import libcore.io.OsConstants; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; @@ -87,7 +87,7 @@ public class FileUtils { */ public static int setPermissions(String path, int mode, int uid, int gid) { try { - Libcore.os.chmod(path, mode); + Os.chmod(path, mode); } catch (ErrnoException e) { Slog.w(TAG, "Failed to chmod(" + path + "): " + e); return e.errno; @@ -95,7 +95,7 @@ public class FileUtils { if (uid >= 0 || gid >= 0) { try { - Libcore.os.chown(path, uid, gid); + Os.chown(path, uid, gid); } catch (ErrnoException e) { Slog.w(TAG, "Failed to chown(" + path + "): " + e); return e.errno; @@ -115,7 +115,7 @@ public class FileUtils { */ public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) { try { - Libcore.os.fchmod(fd, mode); + Os.fchmod(fd, mode); } catch (ErrnoException e) { Slog.w(TAG, "Failed to fchmod(): " + e); return e.errno; @@ -123,7 +123,7 @@ public class FileUtils { if (uid >= 0 || gid >= 0) { try { - Libcore.os.fchown(fd, uid, gid); + Os.fchown(fd, uid, gid); } catch (ErrnoException e) { Slog.w(TAG, "Failed to fchown(): " + e); return e.errno; @@ -138,7 +138,7 @@ public class FileUtils { */ public static int getUid(String path) { try { - return Libcore.os.stat(path).st_uid; + return Os.stat(path).st_uid; } catch (ErrnoException e) { return -1; } @@ -357,4 +357,26 @@ public class FileUtils { } } } + + /** + * Test if a file lives under the given directory, either as a direct child + * or a distant grandchild. + * <p> + * Both files <em>must</em> have been resolved using + * {@link File#getCanonicalFile()} to avoid symlink or path traversal + * attacks. + */ + public static boolean contains(File dir, File file) { + String dirPath = dir.getAbsolutePath(); + String filePath = file.getAbsolutePath(); + + if (dirPath.equals(filePath)) { + return true; + } + + if (!dirPath.endsWith("/")) { + dirPath += "/"; + } + return filePath.startsWith(dirPath); + } } diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java index ee7a4c6..6cec55a 100644 --- a/core/java/android/os/MemoryFile.java +++ b/core/java/android/os/MemoryFile.java @@ -63,12 +63,17 @@ public class MemoryFile * Allocates a new ashmem region. The region is initially not purgable. * * @param name optional name for the file (can be null). - * @param length of the memory file in bytes. + * @param length of the memory file in bytes, must be non-negative. * @throws IOException if the memory file could not be created. */ public MemoryFile(String name, int length) throws IOException { mLength = length; - mFD = native_open(name, length); + if (length >= 0) { + mFD = native_open(name, length); + } else { + throw new IOException("Invalid length: " + length); + } + if (length > 0) { mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE); } else { diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 5273c20..59795da 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -16,24 +16,24 @@ package android.os; -import static libcore.io.OsConstants.AF_UNIX; -import static libcore.io.OsConstants.SEEK_SET; -import static libcore.io.OsConstants.SOCK_STREAM; -import static libcore.io.OsConstants.S_ISLNK; -import static libcore.io.OsConstants.S_ISREG; +import static android.system.OsConstants.AF_UNIX; +import static android.system.OsConstants.SEEK_SET; +import static android.system.OsConstants.SOCK_STREAM; +import static android.system.OsConstants.S_ISLNK; +import static android.system.OsConstants.S_ISREG; import android.content.BroadcastReceiver; import android.content.ContentProvider; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; +import android.system.StructStat; import android.util.Log; import dalvik.system.CloseGuard; -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; import libcore.io.Memory; -import libcore.io.OsConstants; -import libcore.io.StructStat; import java.io.Closeable; import java.io.File; @@ -42,6 +42,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InterruptedIOException; import java.net.DatagramSocket; import java.net.Socket; import java.nio.ByteOrder; @@ -260,7 +261,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { */ public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException { try { - final FileDescriptor fd = Libcore.os.dup(orig); + final FileDescriptor fd = Os.dup(orig); return new ParcelFileDescriptor(fd); } catch (ErrnoException e) { throw e.rethrowAsIOException(); @@ -296,7 +297,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { original.setInt$(fd); try { - final FileDescriptor dup = Libcore.os.dup(original); + final FileDescriptor dup = Os.dup(original); return new ParcelFileDescriptor(dup); } catch (ErrnoException e) { throw e.rethrowAsIOException(); @@ -358,7 +359,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { */ public static ParcelFileDescriptor[] createPipe() throws IOException { try { - final FileDescriptor[] fds = Libcore.os.pipe(); + final FileDescriptor[] fds = Os.pipe(); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fds[0]), new ParcelFileDescriptor(fds[1]) }; @@ -380,7 +381,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { public static ParcelFileDescriptor[] createReliablePipe() throws IOException { try { final FileDescriptor[] comm = createCommSocketPair(); - final FileDescriptor[] fds = Libcore.os.pipe(); + final FileDescriptor[] fds = Os.pipe(); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fds[0], comm[0]), new ParcelFileDescriptor(fds[1], comm[1]) }; @@ -397,7 +398,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { try { final FileDescriptor fd0 = new FileDescriptor(); final FileDescriptor fd1 = new FileDescriptor(); - Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); + Os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fd0), new ParcelFileDescriptor(fd1) }; @@ -420,7 +421,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { final FileDescriptor[] comm = createCommSocketPair(); final FileDescriptor fd0 = new FileDescriptor(); final FileDescriptor fd1 = new FileDescriptor(); - Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); + Os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fd0, comm[0]), new ParcelFileDescriptor(fd1, comm[1]) }; @@ -433,7 +434,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { try { final FileDescriptor comm1 = new FileDescriptor(); final FileDescriptor comm2 = new FileDescriptor(); - Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2); + Os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2); IoUtils.setBlocking(comm1, false); IoUtils.setBlocking(comm2, false); return new FileDescriptor[] { comm1, comm2 }; @@ -519,7 +520,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { return mWrapped.getStatSize(); } else { try { - final StructStat st = Libcore.os.fstat(mFd); + final StructStat st = Os.fstat(mFd); if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { return st.st_size; } else { @@ -542,7 +543,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { return mWrapped.seekTo(pos); } else { try { - return Libcore.os.lseek(mFd, pos, SEEK_SET); + return Os.lseek(mFd, pos, SEEK_SET); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } @@ -694,10 +695,13 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { writePtr += len; } - Libcore.os.write(mCommFd, buf, 0, writePtr); + Os.write(mCommFd, buf, 0, writePtr); } catch (ErrnoException e) { // Reporting status is best-effort Log.w(TAG, "Failed to report status: " + e); + } catch (InterruptedIOException e) { + // Reporting status is best-effort + Log.w(TAG, "Failed to report status: " + e); } } finally { @@ -708,7 +712,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { private static Status readCommStatus(FileDescriptor comm, byte[] buf) { try { - final int n = Libcore.os.read(comm, buf, 0, buf.length); + final int n = Os.read(comm, buf, 0, buf.length); if (n == 0) { // EOF means they're dead return new Status(Status.DEAD); @@ -728,6 +732,9 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { Log.d(TAG, "Failed to read status; assuming dead: " + e); return new Status(Status.DEAD); } + } catch (InterruptedIOException e) { + Log.d(TAG, "Failed to read status; assuming dead: " + e); + return new Status(Status.DEAD); } } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 631edd6..28797ce 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -16,27 +16,32 @@ package android.os; -import android.net.LocalSocketAddress; import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.system.Os; import android.util.Log; -import dalvik.system.Zygote; - +import com.android.internal.os.Zygote; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.IOException; import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; - -import libcore.io.Libcore; +import java.util.Arrays; +import java.util.List; /*package*/ class ZygoteStartFailedEx extends Exception { - /** - * Something prevented the zygote process startup from happening normally - */ + ZygoteStartFailedEx(String s) { + super(s); + } - ZygoteStartFailedEx() {}; - ZygoteStartFailedEx(String s) {super(s);} - ZygoteStartFailedEx(Throwable cause) {super(cause);} + ZygoteStartFailedEx(Throwable cause) { + super(cause); + } + + ZygoteStartFailedEx(String s, Throwable cause) { + super(s, cause); + } } /** @@ -45,19 +50,15 @@ import libcore.io.Libcore; public class Process { private static final String LOG_TAG = "Process"; - private static final String ZYGOTE_SOCKET = "zygote"; - /** - * Name of a process for running the platform's media services. - * {@hide} + * @hide for internal use only. */ - public static final String ANDROID_SHARED_MEDIA = "com.android.process.media"; + public static final String ZYGOTE_SOCKET = "zygote"; /** - * Name of the process that Google content providers can share. - * {@hide} + * @hide for internal use only. */ - public static final String GOOGLE_SHARED_APP_CONTENT = "com.google.process.content"; + public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary"; /** * Defines the UID/GID under which system code runs. @@ -344,15 +345,85 @@ public class Process { public static final int SIGNAL_QUIT = 3; public static final int SIGNAL_KILL = 9; public static final int SIGNAL_USR1 = 10; - - // State for communicating with zygote process - static LocalSocket sZygoteSocket; - static DataInputStream sZygoteInputStream; - static BufferedWriter sZygoteWriter; + /** + * State for communicating with the zygote process. + * + * @hide for internal use only. + */ + public static class ZygoteState { + final LocalSocket socket; + final DataInputStream inputStream; + final BufferedWriter writer; + final List<String> abiList; + + boolean mClosed; + + private ZygoteState(LocalSocket socket, DataInputStream inputStream, + BufferedWriter writer, List<String> abiList) { + this.socket = socket; + this.inputStream = inputStream; + this.writer = writer; + this.abiList = abiList; + } + + public static ZygoteState connect(String socketAddress) throws IOException { + DataInputStream zygoteInputStream = null; + BufferedWriter zygoteWriter = null; + final LocalSocket zygoteSocket = new LocalSocket(); + + try { + zygoteSocket.connect(new LocalSocketAddress(socketAddress, + LocalSocketAddress.Namespace.RESERVED)); - /** true if previous zygote open failed */ - static boolean sPreviousZygoteOpenFailed; + zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); + + zygoteWriter = new BufferedWriter(new OutputStreamWriter( + zygoteSocket.getOutputStream()), 256); + } catch (IOException ex) { + try { + zygoteSocket.close(); + } catch (IOException ignore) { + } + + throw ex; + } + + String abiListString = getAbiList(zygoteWriter, zygoteInputStream); + Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString); + + return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter, + Arrays.asList(abiListString.split(","))); + } + + boolean matches(String abi) { + return abiList.contains(abi); + } + + public void close() { + try { + socket.close(); + } catch (IOException ex) { + Log.e(LOG_TAG,"I/O exception on routine close", ex); + } + + mClosed = true; + } + + boolean isClosed() { + return mClosed; + } + } + + /** + * The state of the connection to the primary zygote. + */ + static ZygoteState primaryZygoteState; + + /** + * The state of the connection to the secondary zygote. + */ + static ZygoteState secondaryZygoteState; /** * Start a new process. @@ -378,6 +449,7 @@ public class Process { * @param debugFlags Additional flags. * @param targetSdkVersion The target SDK version for the app. * @param seInfo null-ok SELinux information for the new process. + * @param abi non-null the ABI this app should be started with. * @param zygoteArgs Additional arguments to supply to the zygote process. * * @return An object that describes the result of the attempt to start the process. @@ -391,10 +463,12 @@ public class Process { int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, + String abi, String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, - debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs); + debugFlags, mountExternal, targetSdkVersion, seInfo, + abi, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); @@ -407,93 +481,39 @@ public class Process { static final int ZYGOTE_RETRY_MILLIS = 500; /** - * Tries to open socket to Zygote process if not already open. If - * already open, does nothing. May block and retry. - */ - private static void openZygoteSocketIfNeeded() - throws ZygoteStartFailedEx { - - int retryCount; - - if (sPreviousZygoteOpenFailed) { - /* - * If we've failed before, expect that we'll fail again and - * don't pause for retries. - */ - retryCount = 0; - } else { - retryCount = 10; - } - - /* - * See bug #811181: Sometimes runtime can make it up before zygote. - * Really, we'd like to do something better to avoid this condition, - * but for now just wait a bit... - */ - for (int retry = 0 - ; (sZygoteSocket == null) && (retry < (retryCount + 1)) - ; retry++ ) { - - if (retry > 0) { - try { - Log.i("Zygote", "Zygote not up yet, sleeping..."); - Thread.sleep(ZYGOTE_RETRY_MILLIS); - } catch (InterruptedException ex) { - // should never happen - } - } - - try { - sZygoteSocket = new LocalSocket(); - - sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET, - LocalSocketAddress.Namespace.RESERVED)); - - sZygoteInputStream - = new DataInputStream(sZygoteSocket.getInputStream()); - - sZygoteWriter = - new BufferedWriter( - new OutputStreamWriter( - sZygoteSocket.getOutputStream()), - 256); - - Log.i("Zygote", "Process: zygote socket opened"); - - sPreviousZygoteOpenFailed = false; - break; - } catch (IOException ex) { - if (sZygoteSocket != null) { - try { - sZygoteSocket.close(); - } catch (IOException ex2) { - Log.e(LOG_TAG,"I/O exception on close after exception", - ex2); - } - } - - sZygoteSocket = null; - } - } - - if (sZygoteSocket == null) { - sPreviousZygoteOpenFailed = true; - throw new ZygoteStartFailedEx("connect failed"); - } + * Queries the zygote for the list of ABIS it supports. + * + * @throws ZygoteStartFailedEx if the query failed. + */ + private static String getAbiList(BufferedWriter writer, DataInputStream inputStream) + throws IOException { + // Each query starts with the argument count (1 in this case) + writer.write("1"); + // ... followed by a new-line. + writer.newLine(); + // ... followed by our only argument. + writer.write("--query-abi-list"); + writer.newLine(); + writer.flush(); + + // The response is a length prefixed stream of ASCII bytes. + int numBytes = inputStream.readInt(); + byte[] bytes = new byte[numBytes]; + inputStream.readFully(bytes); + + return new String(bytes, StandardCharsets.US_ASCII); } /** * Sends an argument list to the zygote process, which starts a new child * and returns the child's pid. Please note: the present implementation * replaces newlines in the argument list with spaces. - * @param args argument list - * @return An object that describes the result of the attempt to start the process. + * * @throws ZygoteStartFailedEx if process start failed for any reason */ - private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args) + private static ProcessStartResult zygoteSendArgsAndGetResult( + ZygoteState zygoteState, ArrayList<String> args) throws ZygoteStartFailedEx { - openZygoteSocketIfNeeded(); - try { /** * See com.android.internal.os.ZygoteInit.readArgumentList() @@ -505,9 +525,11 @@ public class Process { * the child or -1 on failure, followed by boolean to * indicate whether a wrapper process was used. */ + final BufferedWriter writer = zygoteState.writer; + final DataInputStream inputStream = zygoteState.inputStream; - sZygoteWriter.write(Integer.toString(args.size())); - sZygoteWriter.newLine(); + writer.write(Integer.toString(args.size())); + writer.newLine(); int sz = args.size(); for (int i = 0; i < sz; i++) { @@ -516,32 +538,22 @@ public class Process { throw new ZygoteStartFailedEx( "embedded newlines not allowed"); } - sZygoteWriter.write(arg); - sZygoteWriter.newLine(); + writer.write(arg); + writer.newLine(); } - sZygoteWriter.flush(); + writer.flush(); // Should there be a timeout on this? ProcessStartResult result = new ProcessStartResult(); - result.pid = sZygoteInputStream.readInt(); + result.pid = inputStream.readInt(); if (result.pid < 0) { throw new ZygoteStartFailedEx("fork() failed"); } - result.usingWrapper = sZygoteInputStream.readBoolean(); + result.usingWrapper = inputStream.readBoolean(); return result; } catch (IOException ex) { - try { - if (sZygoteSocket != null) { - sZygoteSocket.close(); - } - } catch (IOException ex2) { - // we're going to fail anyway - Log.e(LOG_TAG,"I/O exception on routine close", ex2); - } - - sZygoteSocket = null; - + zygoteState.close(); throw new ZygoteStartFailedEx(ex); } } @@ -558,6 +570,7 @@ public class Process { * @param debugFlags Additional flags. * @param targetSdkVersion The target SDK version for the app. * @param seInfo null-ok SELinux information for the new process. + * @param abi the ABI the process should use. * @param extraArgs Additional arguments to supply to the zygote process. * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason @@ -569,6 +582,7 @@ public class Process { int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, + String abi, String[] extraArgs) throws ZygoteStartFailedEx { synchronized(Process.class) { @@ -636,10 +650,43 @@ public class Process { } } - return zygoteSendArgsAndGetResult(argsForZygote); + return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); } } - + + /** + * Tries to open socket to Zygote process if not already open. If + * already open, does nothing. May block and retry. + */ + private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx { + if (primaryZygoteState == null || primaryZygoteState.isClosed()) { + try { + primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET); + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); + } + } + + if (primaryZygoteState.matches(abi)) { + return primaryZygoteState; + } + + // The primary zygote didn't match. Try the secondary. + if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { + try { + secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET); + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); + } + } + + if (secondaryZygoteState.matches(abi)) { + return secondaryZygoteState; + } + + throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); + } + /** * Returns elapsed milliseconds of the time this process has run. * @return Returns the number of milliseconds this process has return. @@ -651,7 +698,7 @@ public class Process { * {@link #killProcess} and {@link #sendSignal}. */ public static final int myPid() { - return Libcore.os.getpid(); + return Os.getpid(); } /** @@ -659,7 +706,7 @@ public class Process { * @hide */ public static final int myPpid() { - return Libcore.os.getppid(); + return Os.getppid(); } /** @@ -667,7 +714,7 @@ public class Process { * {@link #setThreadPriority(int, int)}. */ public static final int myTid() { - return Libcore.os.gettid(); + return Os.gettid(); } /** @@ -677,7 +724,7 @@ public class Process { * a uid identifies a specific app sandbox in a specific user. */ public static final int myUid() { - return Libcore.os.getuid(); + return Os.getuid(); } /** diff --git a/core/java/android/os/StatFs.java b/core/java/android/os/StatFs.java index 9e9521a..13e9a15 100644 --- a/core/java/android/os/StatFs.java +++ b/core/java/android/os/StatFs.java @@ -16,9 +16,9 @@ package android.os; -import libcore.io.ErrnoException; -import libcore.io.Libcore; -import libcore.io.StructStatVfs; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStatVfs; /** * Retrieve overall information about the space on a filesystem. This is a @@ -41,7 +41,7 @@ public class StatFs { private static StructStatVfs doStat(String path) { try { - return Libcore.os.statvfs(path); + return Os.statvfs(path); } catch (ErrnoException e) { throw new IllegalArgumentException("Invalid path: " + path, e); } diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 86d3cf8..39775b6 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -17,7 +17,7 @@ package android.provider; import static android.net.TrafficStats.KB_IN_BYTES; -import static libcore.io.OsConstants.SEEK_SET; +import static android.system.OsConstants.SEEK_SET; import android.content.ContentProviderClient; import android.content.ContentResolver; @@ -38,11 +38,11 @@ import android.os.OperationCanceledException; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.OnCloseListener; import android.os.RemoteException; +import android.system.ErrnoException; +import android.system.Os; import android.util.Log; -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; import java.io.BufferedInputStream; import java.io.File; @@ -699,7 +699,7 @@ public final class DocumentsContract { // optimal decode path; otherwise fall back to buffering. BufferedInputStream is = null; try { - Libcore.os.lseek(fd, offset, SEEK_SET); + Os.lseek(fd, offset, SEEK_SET); } catch (ErrnoException e) { is = new BufferedInputStream(new FileInputStream(fd), THUMBNAIL_BUFFER_SIZE); is.mark(THUMBNAIL_BUFFER_SIZE); @@ -725,7 +725,7 @@ public final class DocumentsContract { bitmap = BitmapFactory.decodeStream(is, null, opts); } else { try { - Libcore.os.lseek(fd, offset, SEEK_SET); + Os.lseek(fd, offset, SEEK_SET); } catch (ErrnoException e) { e.rethrowAsIOException(); } diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java index 29c0ba2..aefced8 100644 --- a/core/java/android/util/EventLog.java +++ b/core/java/android/util/EventLog.java @@ -56,16 +56,18 @@ public class EventLog { public static final class Event { private final ByteBuffer mBuffer; - // Layout of event log entry received from kernel. + // Layout of event log entry received from Android logger. + // see system/core/include/log/logger.h private static final int LENGTH_OFFSET = 0; + private static final int HEADER_SIZE_OFFSET = 2; private static final int PROCESS_OFFSET = 4; private static final int THREAD_OFFSET = 8; private static final int SECONDS_OFFSET = 12; private static final int NANOSECONDS_OFFSET = 16; - private static final int PAYLOAD_START = 20; - private static final int TAG_OFFSET = 20; - private static final int DATA_START = 24; + // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET + private static final int V1_PAYLOAD_START = 20; + private static final int DATA_OFFSET = 4; // Value types private static final byte INT_TYPE = 0; @@ -97,14 +99,22 @@ public class EventLog { /** @return the type tag code of the entry */ public int getTag() { - return mBuffer.getInt(TAG_OFFSET); + int offset = mBuffer.getShort(HEADER_SIZE_OFFSET); + if (offset == 0) { + offset = V1_PAYLOAD_START; + } + return mBuffer.getInt(offset); } /** @return one of Integer, Long, String, null, or Object[] of same. */ public synchronized Object getData() { try { - mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET)); - mBuffer.position(DATA_START); // Just after the tag. + int offset = mBuffer.getShort(HEADER_SIZE_OFFSET); + if (offset == 0) { + offset = V1_PAYLOAD_START; + } + mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET)); + mBuffer.position(offset + DATA_OFFSET); // Just after the tag. return decodeObject(); } catch (IllegalArgumentException e) { Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e); diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java index abd173a..2b81072 100644 --- a/core/java/android/util/Log.java +++ b/core/java/android/util/Log.java @@ -352,6 +352,7 @@ public final class Log { /** @hide */ public static final int LOG_ID_RADIO = 1; /** @hide */ public static final int LOG_ID_EVENTS = 2; /** @hide */ public static final int LOG_ID_SYSTEM = 3; + /** @hide */ public static final int LOG_ID_CRASH = 4; /** @hide */ public static native int println_native(int bufID, int priority, String tag, String msg); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index f09a111..9097a6c 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -1555,10 +1555,6 @@ public abstract class HardwareRenderer { } private DisplayList buildDisplayList(View view, HardwareCanvas canvas) { - if (mDrawDelta <= 0) { - return view.mDisplayList; - } - view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) == View.PFLAG_INVALIDATED; view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index bc0d7e3..0f633a0 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -660,7 +660,7 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES)); } - public boolean attachFunctor(int functor) { + public boolean attachFunctor(long functor) { //noinspection SimplifiableIfStatement if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { return mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor); @@ -668,12 +668,17 @@ public final class ViewRootImpl implements ViewParent, return false; } - public void detachFunctor(int functor) { + public void detachFunctor(long functor) { if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.detachFunctor(functor); } } + public boolean invokeFunctor(long functor, boolean waitForCompletion) { + // stub + return false; + } + private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { mAttachInfo.mHardwareAccelerated = false; mAttachInfo.mHardwareAccelerationRequested = false; diff --git a/core/java/android/webkit/ClientCertRequest.java b/core/java/android/webkit/ClientCertRequest.java new file mode 100644 index 0000000..8951786 --- /dev/null +++ b/core/java/android/webkit/ClientCertRequest.java @@ -0,0 +1,80 @@ +/* + * 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 android.webkit; + +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +/** + * ClientCertRequest: The user receives an instance of this class as + * a parameter of {@link WebViewClient#onReceivedClientCertRequest}. + * The request includes the parameters to choose the client certificate, + * such as the host name and the port number requesting the cert, the acceptable + * key types and the principals. + * + * The user should call one of the interface methods to indicate how to deal + * with the client certificate request. All methods should be called on + * UI thread. + * + * WebView caches the {@link #proceed} and {@link #cancel} responses in memory + * and uses them to handle future client certificate requests for the same + * host/port pair. The user can clear the cached data using + * {@link WebView#clearClientCertPreferences}. + * + * TODO(sgurun) unhide + * @hide + */ +public interface ClientCertRequest { + /** + * Returns the acceptable types of asymmetric keys (can be null). + */ + public String[] getKeyTypes(); + + /** + * Returns the acceptable certificate issuers for the certificate + * matching the private key (can be null). + */ + public Principal[] getPrincipals(); + + /** + * Returns the host name of the server requesting the certificate. + */ + public String getHost(); + + /** + * Returns the port number of the server requesting the certificate. + */ + public int getPort(); + + /** + * Proceed with the specified private key and client certificate chain. + * Remember the user's positive choice and use it for future requests. + */ + public void proceed(PrivateKey privateKey, X509Certificate[] chain); + + /** + * Ignore the request for now. Do not remember user's choice. + */ + public void ignore(); + + /** + * Cancel this request. Remember the user's choice and use it for + * future requests. + */ + public void cancel(); +} diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index e8974c6..fb842ff 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -204,6 +204,30 @@ public class WebViewClient { handler.cancel(); } + /** + * Notify the host application to handle a SSL client certificate + * request. The host application is responsible for showing the UI + * if desired and providing the keys. There are three ways to + * respond: proceed(), cancel() or ignore(). Webview remembers the + * response if proceed() or cancel() is called and does not + * call onReceivedClientCertRequest() again for the same host and port + * pair. Webview does not remember the response if ignore() is called. + * + * This method is called on the UI thread. During the callback, the + * connection is suspended. + * + * The default behavior is to cancel, returning no client certificate. + * + * @param view The WebView that is initiating the callback + * @param request An instance of a {@link ClientCertRequest} + * + * TODO(sgurun) unhide + * @hide + */ + public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) { + request.cancel(); + } + /** * Notifies the host application that the WebView received an HTTP * authentication request. The host application can use the supplied diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index c0fde2e..26c5732 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -1944,7 +1944,16 @@ public class NumberPicker extends LinearLayout { , '\u0669', // Extended Arabic-Indic '\u06f0', '\u06f1', '\u06f2', '\u06f3', '\u06f4', '\u06f5', '\u06f6', '\u06f7', '\u06f8' - , '\u06f9' + , '\u06f9', + // Hindi and Marathi (Devanagari script) + '\u0966', '\u0967', '\u0968', '\u0969', '\u096a', '\u096b', '\u096c', '\u096d', '\u096e' + , '\u096f', + // Bengali + '\u09e6', '\u09e7', '\u09e8', '\u09e9', '\u09ea', '\u09eb', '\u09ec', '\u09ed', '\u09ee' + , '\u09ef', + // Kannada + '\u0ce6', '\u0ce7', '\u0ce8', '\u0ce9', '\u0cea', '\u0ceb', '\u0cec', '\u0ced', '\u0cee' + , '\u0cef' }; /** diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 7a9809f..3f35875 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -5147,12 +5147,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int width = mRight - mLeft; final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight(); final float dx = mLayout.getLineRight(0) - (width - padding); - canvas.translate(isLayoutRtl ? -dx : +dx, 0.0f); + canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); } if (mMarquee != null && mMarquee.isRunning()) { final float dx = -mMarquee.getScroll(); - canvas.translate(isLayoutRtl ? -dx : +dx, 0.0f); + canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); } } @@ -5166,8 +5166,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (mMarquee != null && mMarquee.shouldDrawGhost()) { - final int dx = (int) mMarquee.getGhostOffset(); - canvas.translate(isLayoutRtl ? -dx : dx, 0.0f); + final float dx = mMarquee.getGhostOffset(); + canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 70f90d3..1eda373 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -27,8 +27,9 @@ public class ChooserActivity extends ResolverActivity { Intent intent = getIntent(); Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT); if (!(targetParcelable instanceof Intent)) { - Log.w("ChooseActivity", "Target is not an intent: " + targetParcelable); + Log.w("ChooserActivity", "Target is not an intent: " + targetParcelable); finish(); + super.onCreate(null); return; } Intent target = (Intent)targetParcelable; @@ -42,9 +43,10 @@ public class ChooserActivity extends ResolverActivity { initialIntents = new Intent[pa.length]; for (int i=0; i<pa.length; i++) { if (!(pa[i] instanceof Intent)) { - Log.w("ChooseActivity", "Initial intent #" + i + Log.w("ChooserActivity", "Initial intent #" + i + " not an Intent: " + pa[i]); finish(); + super.onCreate(null); return; } initialIntents[i] = (Intent)pa[i]; diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index 6d65782..ba419f9 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -16,7 +16,7 @@ package com.android.internal.content; -import android.os.Build; +import android.content.pm.PackageManager; import android.util.Slog; import java.io.File; @@ -31,38 +31,76 @@ public class NativeLibraryHelper { private static final boolean DEBUG_NATIVE = false; - private static native long nativeSumNativeBinaries(String file, String cpuAbi, String cpuAbi2); + /** + * A handle to an opened APK. Used as input to the various NativeLibraryHelper + * methods. Allows us to scan and parse the APK exactly once instead of doing + * it multiple times. + * + * @hide + */ + public static class ApkHandle { + final String apkPath; + final long apkHandle; + + public ApkHandle(String path) { + apkPath = path; + apkHandle = nativeOpenApk(apkPath); + } + + public ApkHandle(File apkFile) { + apkPath = apkFile.getPath(); + apkHandle = nativeOpenApk(apkPath); + } + + public void close() { + nativeClose(apkHandle); + } + } + + + private static native long nativeOpenApk(String path); + private static native void nativeClose(long handle); + + private static native long nativeSumNativeBinaries(long handle, String cpuAbi); /** - * Sums the size of native binaries in an APK. + * Sums the size of native binaries in an APK for a given ABI. * - * @param apkFile APK file to scan for native libraries * @return size of all native binary files in bytes */ - public static long sumNativeBinariesLI(File apkFile) { - final String cpuAbi = Build.CPU_ABI; - final String cpuAbi2 = Build.CPU_ABI2; - return nativeSumNativeBinaries(apkFile.getPath(), cpuAbi, cpuAbi2); + public static long sumNativeBinariesLI(ApkHandle handle, String abi) { + return nativeSumNativeBinaries(handle.apkHandle, abi); } - private native static int nativeCopyNativeBinaries(String filePath, String sharedLibraryPath, - String cpuAbi, String cpuAbi2); + private native static int nativeCopyNativeBinaries(long handle, + String sharedLibraryPath, String abiToCopy); /** * Copies native binaries to a shared library directory. * - * @param apkFile APK file to scan for native libraries + * @param handle 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 */ - 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); + public static int copyNativeBinariesIfNeededLI(ApkHandle handle, File sharedLibraryDir, + String abi) { + return nativeCopyNativeBinaries(handle.apkHandle, sharedLibraryDir.getPath(), abi); } + /** + * Checks if a given APK contains native code for any of the provided + * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching + * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the + * APK doesn't contain any native code, and + * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match. + */ + public static int findSupportedAbi(ApkHandle handle, String[] supportedAbis) { + return nativeFindSupportedAbi(handle.apkHandle, supportedAbis); + } + + private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis); + // Convenience method to call removeNativeBinariesFromDirLI(File) public static boolean removeNativeBinariesLI(String nativeLibraryPath) { return removeNativeBinariesFromDirLI(new File(nativeLibraryPath)); diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 5538dca..4a26b4b 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -55,6 +55,11 @@ public class RuntimeInit { private static final native void nativeFinishInit(); private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup); + private static int Clog_e(String tag, String msg, Throwable tr) { + return Log.println_native(Log.LOG_ID_CRASH, Log.ERROR, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + /** * Use this to log a message when a thread exits due to an uncaught * exception. The framework catches these for the main threads, so @@ -68,7 +73,7 @@ public class RuntimeInit { mCrashing = true; if (mApplicationObject == null) { - Slog.e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); + Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); } else { StringBuilder message = new StringBuilder(); message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n"); @@ -77,7 +82,7 @@ public class RuntimeInit { message.append("Process: ").append(processName).append(", "); } message.append("PID: ").append(Process.myPid()); - Slog.e(TAG, message.toString(), e); + Clog_e(TAG, message.toString(), e); } // Bring up crash dialog, wait for it to be dismissed @@ -85,9 +90,9 @@ public class RuntimeInit { mApplicationObject, new ApplicationErrorReport.CrashInfo(e)); } catch (Throwable t2) { try { - Slog.e(TAG, "Error reporting crash", t2); + Clog_e(TAG, "Error reporting crash", t2); } catch (Throwable t3) { - // Even Slog.e() fails! Oh well. + // Even Clog_e() fails! Oh well. } } finally { // Try everything to make sure this process goes away. diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index c6b3e7c..3301cbe 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -25,9 +25,6 @@ import java.io.FileOutputStream; import java.io.IOException; import libcore.io.IoUtils; -import libcore.io.Libcore; - -import dalvik.system.Zygote; /** * Startup class for the wrapper process. @@ -95,7 +92,7 @@ public class WrapperInit { * @param niceName The nice name for the application, or null if none. * @param targetSdkVersion The target SDK version for the app. * @param pipeFd The pipe to which the application's pid should be written, or null if none. - * @param args Arguments for {@link RuntimeInit.main}. + * @param args Arguments for {@link RuntimeInit#main}. */ public static void execApplication(String invokeWith, String niceName, int targetSdkVersion, FileDescriptor pipeFd, String[] args) { diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java new file mode 100644 index 0000000..54c532a --- /dev/null +++ b/core/java/com/android/internal/os/Zygote.java @@ -0,0 +1,164 @@ +/* + * 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.internal.os; + + +import dalvik.system.ZygoteHooks; +import android.system.ErrnoException; +import android.system.Os; + +/** @hide */ +public final class Zygote { + /* + * Bit values for "debugFlags" argument. The definitions are duplicated + * in the native code. + */ + + /** enable debugging over JDWP */ + public static final int DEBUG_ENABLE_DEBUGGER = 1; + /** enable JNI checks */ + public static final int DEBUG_ENABLE_CHECKJNI = 1 << 1; + /** enable Java programming language "assert" statements */ + public static final int DEBUG_ENABLE_ASSERT = 1 << 2; + /** disable the JIT compiler */ + public static final int DEBUG_ENABLE_SAFEMODE = 1 << 3; + /** Enable logging of third-party JNI activity. */ + public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4; + + /** No external storage should be mounted. */ + public static final int MOUNT_EXTERNAL_NONE = 0; + /** Single-user external storage should be mounted. */ + public static final int MOUNT_EXTERNAL_SINGLEUSER = 1; + /** Multi-user external storage should be mounted. */ + public static final int MOUNT_EXTERNAL_MULTIUSER = 2; + /** All multi-user external storage should be mounted. */ + public static final int MOUNT_EXTERNAL_MULTIUSER_ALL = 3; + + private static final ZygoteHooks VM_HOOKS = new ZygoteHooks(); + + private Zygote() {} + + /** + * Forks a new VM instance. The current VM must have been started + * with the -Xzygote flag. <b>NOTE: new instance keeps all + * root capabilities. The new process is expected to call capset()</b>. + * + * @param uid the UNIX uid that the new process should setuid() to after + * fork()ing and and before spawning any threads. + * @param gid the UNIX gid that the new process should setgid() to after + * fork()ing and and before spawning any threads. + * @param gids null-ok; a list of UNIX gids that the new process should + * setgroups() to after fork and before spawning any threads. + * @param debugFlags bit flags that enable debugging features. + * @param rlimits null-ok an array of rlimit tuples, with the second + * dimension having a length of 3 and representing + * (resource, rlim_cur, rlim_max). These are set via the posix + * setrlimit(2) call. + * @param seInfo null-ok a string specifying SELinux information for + * the new process. + * @param niceName null-ok a string specifying the process name. + * @param fdsToClose an array of ints, holding one or more POSIX + * file descriptor numbers that are to be closed by the child + * (and replaced by /dev/null) after forking. An integer value + * of -1 in any entry in the array means "ignore this one". + * + * @return 0 if this is the child, pid of the child + * if this is the parent, or -1 on error. + */ + public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags, + int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose) { + VM_HOOKS.preFork(); + int pid = nativeForkAndSpecialize( + uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose); + VM_HOOKS.postForkCommon(); + return pid; + } + + native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int debugFlags, + int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose); + + /** + * Special method to start the system server process. In addition to the + * common actions performed in forkAndSpecialize, the pid of the child + * process is recorded such that the death of the child process will cause + * zygote to exit. + * + * @param uid the UNIX uid that the new process should setuid() to after + * fork()ing and and before spawning any threads. + * @param gid the UNIX gid that the new process should setgid() to after + * fork()ing and and before spawning any threads. + * @param gids null-ok; a list of UNIX gids that the new process should + * setgroups() to after fork and before spawning any threads. + * @param debugFlags bit flags that enable debugging features. + * @param rlimits null-ok an array of rlimit tuples, with the second + * dimension having a length of 3 and representing + * (resource, rlim_cur, rlim_max). These are set via the posix + * setrlimit(2) call. + * @param permittedCapabilities argument for setcap() + * @param effectiveCapabilities argument for setcap() + * + * @return 0 if this is the child, pid of the child + * if this is the parent, or -1 on error. + */ + public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags, + int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { + VM_HOOKS.preFork(); + int pid = nativeForkSystemServer( + uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities); + VM_HOOKS.postForkCommon(); + return pid; + } + + native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags, + int[][] rlimits, long permittedCapabilities, long effectiveCapabilities); + + private static void callPostForkChildHooks(int debugFlags) { + VM_HOOKS.postForkChild(debugFlags); + } + + + /** + * Executes "/system/bin/sh -c <command>" using the exec() system call. + * This method throws a runtime exception if exec() failed, otherwise, this + * method never returns. + * + * @param command The shell command to execute. + */ + public static void execShell(String command) { + String[] args = { "/system/bin/sh", "-c", command }; + try { + Os.execv(args[0], args); + } catch (ErrnoException e) { + throw new RuntimeException(e); + } + } + + /** + * Appends quotes shell arguments to the specified string builder. + * The arguments are quoted using single-quotes, escaped if necessary, + * prefixed with a space, and appended to the command. + * + * @param command A string builder for the shell command being constructed. + * @param args An array of argument strings to be quoted and appended to the command. + * @see #execShell(String) + */ + public static void appendQuotedShellArgs(StringBuilder command, String[] args) { + for (String arg : args) { + command.append(" '").append(arg.replace("'", "'\\''")).append("'"); + } + } +} diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index f9a1f89..0c48368 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -21,11 +21,10 @@ import android.net.LocalSocket; import android.os.Process; import android.os.SELinux; import android.os.SystemProperties; +import android.system.ErrnoException; +import android.system.Os; import android.util.Log; - import dalvik.system.PathClassLoader; -import dalvik.system.Zygote; - import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -35,11 +34,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; - -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; /** * A connection that can make spawn requests. @@ -60,7 +57,7 @@ class ZygoteConnection { private static final int CONNECTION_TIMEOUT_MILLIS = 1000; /** max number of arguments that a connection can specify */ - private static final int MAX_ZYGOTE_ARGC=1024; + private static final int MAX_ZYGOTE_ARGC = 1024; /** * The command socket. @@ -74,15 +71,18 @@ class ZygoteConnection { private final BufferedReader mSocketReader; private final Credentials peer; private final String peerSecurityContext; + private final String abiList; /** * Constructs instance from connected socket. * * @param socket non-null; connected socket + * @param abiList non-null; a list of ABIs this zygote supports. * @throws IOException */ - ZygoteConnection(LocalSocket socket) throws IOException { + ZygoteConnection(LocalSocket socket, String abiList) throws IOException { mSocket = socket; + this.abiList = abiList; mSocketOutStream = new DataOutputStream(socket.getOutputStream()); @@ -112,43 +112,6 @@ class ZygoteConnection { } /** - * Reads start commands from an open command socket. - * Start commands are presently a pair of newline-delimited lines - * indicating a) class to invoke main() on b) nice name to set argv[0] to. - * Continues to read commands and forkAndSpecialize children until - * the socket is closed. This method is used in ZYGOTE_FORK_MODE - * - * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main() - * method in child process - */ - void run() throws ZygoteInit.MethodAndArgsCaller { - - int loopCount = ZygoteInit.GC_LOOP_COUNT; - - while (true) { - /* - * Call gc() before we block in readArgumentList(). - * It's work that has to be done anyway, and it's better - * to avoid making every child do it. It will also - * madvise() any free memory as a side-effect. - * - * Don't call it every time, because walking the entire - * heap is a lot of overhead to free a few hundred bytes. - */ - if (loopCount <= 0) { - ZygoteInit.gc(); - loopCount = ZygoteInit.GC_LOOP_COUNT; - } else { - loopCount--; - } - - if (runOnce()) { - break; - } - } - } - - /** * Reads one start command from the command socket. If successful, * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller} * exception is thrown in that child while in the parent process, @@ -197,6 +160,11 @@ class ZygoteConnection { try { parsedArgs = new Arguments(args); + + if (parsedArgs.abiListQuery) { + return handleAbiListQuery(); + } + if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) { throw new ZygoteSecurityException("Client may not specify capabilities: " + "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) + @@ -218,7 +186,7 @@ class ZygoteConnection { } if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) { - FileDescriptor[] pipeFds = Libcore.os.pipe(); + FileDescriptor[] pipeFds = Os.pipe(); childPipeFd = pipeFds[1]; serverPipeFd = pipeFds[0]; ZygoteInit.setCloseOnExec(serverPipeFd, true); @@ -288,6 +256,18 @@ class ZygoteConnection { } } + private boolean handleAbiListQuery() { + try { + final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII); + mSocketOutStream.writeInt(abiListBytes.length); + mSocketOutStream.write(abiListBytes); + return false; + } catch (IOException ioe) { + Log.e(TAG, "Error writing to command socket", ioe); + return true; + } + } + /** * Closes socket associated with this connection. */ @@ -389,6 +369,11 @@ class ZygoteConnection { String remainingArgs[]; /** + * Whether the current arguments constitute an ABI list query. + */ + boolean abiListQuery; + + /** * Constructs instance and parses args * @param args zygote command-line args * @throws IllegalArgumentException @@ -541,6 +526,8 @@ class ZygoteConnection { mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER; } else if (arg.equals("--mount-external-multiuser-all")) { mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL; + } else if (arg.equals("--query-abi-list")) { + abiListQuery = true; } else { break; } @@ -807,7 +794,7 @@ class ZygoteConnection { /** * Applies invoke-with system properties to the zygote arguments. * - * @param parsedArgs non-null; zygote args + * @param args non-null; zygote args */ public static void applyInvokeWithSystemProperty(Arguments args) { if (args.invokeWith == null && args.niceName != null) { @@ -976,7 +963,7 @@ class ZygoteConnection { mSocketOutStream.writeInt(pid); mSocketOutStream.writeBoolean(usingWrapper); } catch (IOException ex) { - Log.e(TAG, "Error reading from command socket", ex); + Log.e(TAG, "Error writing to command socket", ex); return true; } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index cc24ff7..7d8066c 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -16,27 +16,27 @@ package com.android.internal.os; -import static libcore.io.OsConstants.S_IRWXG; -import static libcore.io.OsConstants.S_IRWXO; +import static android.system.OsConstants.S_IRWXG; +import static android.system.OsConstants.S_IRWXO; import android.content.res.Resources; import android.content.res.TypedArray; import android.net.LocalServerSocket; import android.opengl.EGL14; +import android.os.Build; import android.os.Debug; import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; +import android.system.Os; +import android.system.OsConstants; import android.util.EventLog; import android.util.Log; import dalvik.system.VMRuntime; -import dalvik.system.Zygote; import libcore.io.IoUtils; -import libcore.io.Libcore; -import libcore.io.OsConstants; import java.io.BufferedReader; import java.io.FileDescriptor; @@ -65,7 +65,7 @@ public class ZygoteInit { private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload"; - private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote"; + private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020; private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030; @@ -73,8 +73,9 @@ public class ZygoteInit { /** when preloading, GC after allocating this many bytes */ private static final int PRELOAD_GC_THRESHOLD = 50000; - public static final String USAGE_STRING = - " <\"start-system-server\"|\"\" for startSystemServer>"; + private static final String ABI_LIST_ARG = "--abi-list="; + + private static final String SOCKET_NAME_ARG = "--socket-name="; private static LocalServerSocket sServerSocket; @@ -151,15 +152,15 @@ public class ZygoteInit { * * @throws RuntimeException when open fails */ - private static void registerZygoteSocket() { + private static void registerZygoteSocket(String socketName) { if (sServerSocket == null) { int fileDesc; + final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; try { - String env = System.getenv(ANDROID_SOCKET_ENV); + String env = System.getenv(fullSocketName); fileDesc = Integer.parseInt(env); } catch (RuntimeException ex) { - throw new RuntimeException( - ANDROID_SOCKET_ENV + " unset or invalid", ex); + throw new RuntimeException(fullSocketName + " unset or invalid", ex); } try { @@ -176,9 +177,9 @@ public class ZygoteInit { * Waits for and accepts a single command connection. Throws * RuntimeException on failure. */ - private static ZygoteConnection acceptCommandPeer() { + private static ZygoteConnection acceptCommandPeer(String abiList) { try { - return new ZygoteConnection(sServerSocket.accept()); + return new ZygoteConnection(sServerSocket.accept(), abiList); } catch (IOException ex) { throw new RuntimeException( "IOException during accept()", ex); @@ -473,7 +474,7 @@ public class ZygoteInit { closeServerSocket(); // set umask to 0077 so new files and directories will default to owner-only permissions. - Libcore.os.umask(S_IRWXG | S_IRWXO); + Os.umask(S_IRWXG | S_IRWXO); if (parsedArgs.niceName != null) { Process.setArgV0(parsedArgs.niceName); @@ -496,9 +497,10 @@ public class ZygoteInit { /** * Prepare the arguments and fork for the system server process. */ - private static boolean startSystemServer() + private static boolean startSystemServer(String abiList, String socketName) throws MethodAndArgsCaller, RuntimeException { long capabilities = posixCapabilitiesAsBits( + OsConstants.CAP_BLOCK_SUSPEND, OsConstants.CAP_KILL, OsConstants.CAP_NET_ADMIN, OsConstants.CAP_NET_BIND_SERVICE, @@ -543,6 +545,10 @@ public class ZygoteInit { /* For child process */ if (pid == 0) { + if (hasSecondZygote(abiList)) { + waitForSecondaryZygote(socketName); + } + handleSystemServerProcess(parsedArgs); } @@ -568,7 +574,26 @@ public class ZygoteInit { // Start profiling the zygote initialization. SamplingProfilerIntegration.start(); - registerZygoteSocket(); + boolean startSystemServer = false; + String socketName = "zygote"; + String abiList = null; + for (int i = 1; i < argv.length; i++) { + if ("start-system-server".equals(argv[i])) { + startSystemServer = true; + } else if (argv[i].startsWith(ABI_LIST_ARG)) { + abiList = argv[i].substring(ABI_LIST_ARG.length()); + } else if (argv[i].startsWith(SOCKET_NAME_ARG)) { + socketName = argv[i].substring(SOCKET_NAME_ARG.length()); + } else { + throw new RuntimeException("Unknown command line argument: " + argv[i]); + } + } + + if (abiList == null) { + throw new RuntimeException("No ABI list supplied."); + } + + registerZygoteSocket(socketName); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); preload(); @@ -585,20 +610,12 @@ public class ZygoteInit { // Zygote. Trace.setTracingEnabled(false); - // If requested, start system server directly from Zygote - if (argv.length != 2) { - throw new RuntimeException(argv[0] + USAGE_STRING); - } - - if (argv[1].equals("start-system-server")) { - startSystemServer(); - } else if (!argv[1].equals("")) { - throw new RuntimeException(argv[0] + USAGE_STRING); + if (startSystemServer) { + startSystemServer(abiList, socketName); } Log.i(TAG, "Accepting command socket connections"); - - runSelectLoop(); + runSelectLoop(abiList); closeServerSocket(); } catch (MethodAndArgsCaller caller) { @@ -611,6 +628,36 @@ public class ZygoteInit { } /** + * Return {@code true} if this device configuration has another zygote. + * + * We determine this by comparing the device ABI list with this zygotes + * list. If this zygote supports all ABIs this device supports, there won't + * be another zygote. + */ + private static boolean hasSecondZygote(String abiList) { + return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList); + } + + private static void waitForSecondaryZygote(String socketName) { + String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ? + Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET; + while (true) { + try { + final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName); + zs.close(); + break; + } catch (IOException ioe) { + Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage()); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + } + } + } + + /** * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's * worth at a time. @@ -618,7 +665,7 @@ public class ZygoteInit { * @throws MethodAndArgsCaller in a child process when a main() should * be executed. */ - private static void runSelectLoop() throws MethodAndArgsCaller { + private static void runSelectLoop(String abiList) throws MethodAndArgsCaller { ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); FileDescriptor[] fdArray = new FileDescriptor[4]; @@ -657,7 +704,7 @@ public class ZygoteInit { if (index < 0) { throw new RuntimeException("Error in select()"); } else if (index == 0) { - ZygoteConnection newPeer = acceptCommandPeer(); + ZygoteConnection newPeer = acceptCommandPeer(abiList); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); } else { diff --git a/core/jni/Android.mk b/core/jni/Android.mk index ac70738..bdef428 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -149,7 +149,8 @@ LOCAL_SRC_FILES:= \ android_content_res_ObbScanner.cpp \ android_content_res_Configuration.cpp \ android_animation_PropertyValuesHolder.cpp \ - com_android_internal_net_NetworkStatsFactory.cpp + com_android_internal_net_NetworkStatsFactory.cpp \ + com_android_internal_os_Zygote.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ @@ -223,8 +224,6 @@ LOCAL_SHARED_LIBRARIES += \ # <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private -LOCAL_LDLIBS += -lpthread -ldl - ifeq ($(WITH_MALLOC_LEAK_CHECK),true) LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK endif diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 649968e..fc6cc81 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -177,6 +177,7 @@ extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env); +extern int register_com_android_internal_os_Zygote(JNIEnv *env); static AndroidRuntime* gCurRuntime = NULL; @@ -227,9 +228,10 @@ int register_com_android_internal_os_RuntimeInit(JNIEnv* env) /*static*/ JavaVM* AndroidRuntime::mJavaVM = NULL; - -AndroidRuntime::AndroidRuntime() : - mExitWithoutCleanup(false) +AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) : + mExitWithoutCleanup(false), + mArgBlockStart(argBlockStart), + mArgBlockLength(argBlockLength) { SkGraphics::Init(); // this sets our preference for 16bit images during decode @@ -264,13 +266,17 @@ AndroidRuntime::~AndroidRuntime() return jniRegisterNativeMethods(env, className, gMethods, numMethods); } -status_t AndroidRuntime::callMain(const char* className, - jclass clazz, int argc, const char* const argv[]) +void AndroidRuntime::setArgv0(const char* argv0) { + strlcpy(mArgBlockStart, argv0, mArgBlockLength); +} + +status_t AndroidRuntime::callMain(const String8& className, jclass clazz, + const Vector<String8>& args) { JNIEnv* env; jmethodID methodId; - ALOGD("Calling main entry %s", className); + ALOGD("Calling main entry %s", className.string()); env = getJNIEnv(); if (clazz == NULL || env == NULL) { @@ -279,7 +285,7 @@ status_t AndroidRuntime::callMain(const char* className, methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V"); if (methodId == NULL) { - ALOGE("ERROR: could not find method %s.main(String[])\n", className); + ALOGE("ERROR: could not find method %s.main(String[])\n", className.string()); return UNKNOWN_ERROR; } @@ -290,11 +296,12 @@ status_t AndroidRuntime::callMain(const char* className, jclass stringClass; jobjectArray strArray; + const size_t numArgs = args.size(); stringClass = env->FindClass("java/lang/String"); - strArray = env->NewObjectArray(argc, stringClass, NULL); + strArray = env->NewObjectArray(numArgs, stringClass, NULL); - for (int i = 0; i < argc; i++) { - jstring argStr = env->NewStringUTF(argv[i]); + for (size_t i = 0; i < numArgs; i++) { + jstring argStr = env->NewStringUTF(args[i].string()); env->SetObjectArrayElement(strArray, i, argStr); } @@ -460,6 +467,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) char heapminfreeOptsBuf[sizeof("-XX:HeapMinFree=")-1 + PROPERTY_VALUE_MAX]; char heapmaxfreeOptsBuf[sizeof("-XX:HeapMaxFree=")-1 + PROPERTY_VALUE_MAX]; char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX]; + char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX]; char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX]; char jitcodecachesizeOptsBuf[sizeof("-Xjitcodecachesize:")-1 + PROPERTY_VALUE_MAX]; char dalvikVmLibBuf[PROPERTY_VALUE_MAX]; @@ -613,6 +621,13 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) mOptions.add(opt); } + strcpy(backgroundgcOptsBuf, "-XX:BackgroundGC="); + property_get("dalvik.vm.backgroundgctype", backgroundgcOptsBuf+sizeof("-XX:BackgroundGC=")-1, ""); + if (backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1] != '\0') { + opt.optionString = backgroundgcOptsBuf; + mOptions.add(opt); + } + /* * Enable or disable dexopt features, such as bytecode verification and * calculation of register maps for precise GC. @@ -866,20 +881,23 @@ char* AndroidRuntime::toSlashClassName(const char* className) * Passes the main function two arguments, the class name and the specified * options string. */ -void AndroidRuntime::start(const char* className, const char* options) +void AndroidRuntime::start(const char* className, const Vector<String8>& options) { ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n", className != NULL ? className : "(unknown)"); + static const String8 startSystemServer("start-system-server"); + /* * 'startSystemServer == true' means runtime is obsolete and not run from * init.rc anymore, so we print out the boot start event here. */ - if (strcmp(options, "start-system-server") == 0) { - /* track our progress through the boot sequence */ - const int LOG_BOOT_PROGRESS_START = 3000; - LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, - ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); + for (size_t i = 0; i < options.size(); ++i) { + if (options[i] == startSystemServer) { + /* track our progress through the boot sequence */ + const int LOG_BOOT_PROGRESS_START = 3000; + LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); + } } const char* rootDir = getenv("ANDROID_ROOT"); @@ -920,17 +938,20 @@ void AndroidRuntime::start(const char* className, const char* options) jclass stringClass; jobjectArray strArray; jstring classNameStr; - jstring optionsStr; stringClass = env->FindClass("java/lang/String"); assert(stringClass != NULL); - strArray = env->NewObjectArray(2, stringClass, NULL); + strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL); assert(strArray != NULL); classNameStr = env->NewStringUTF(className); assert(classNameStr != NULL); env->SetObjectArrayElement(strArray, 0, classNameStr); - optionsStr = env->NewStringUTF(options); - env->SetObjectArrayElement(strArray, 1, optionsStr); + + for (size_t i = 0; i < options.size(); ++i) { + jstring optionsStr = env->NewStringUTF(options.itemAt(i).string()); + assert(optionsStr != NULL); + env->SetObjectArrayElement(strArray, i + 1, optionsStr); + } /* * Start VM. This thread becomes the main thread of the VM, and will @@ -1241,6 +1262,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_net_wifi_WifiNative), REG_JNI(register_android_os_MemoryFile), REG_JNI(register_com_android_internal_os_ZygoteInit), + REG_JNI(register_com_android_internal_os_Zygote), REG_JNI(register_android_hardware_Camera), REG_JNI(register_android_hardware_camera2_CameraMetadata), REG_JNI(register_android_hardware_SensorManager), diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp index 799782d..6591d60 100644 --- a/core/jni/android/graphics/YuvToJpegEncoder.cpp +++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp @@ -226,16 +226,17 @@ static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv, jint* imgOffsets = env->GetIntArrayElements(offsets, NULL); jint* imgStrides = env->GetIntArrayElements(strides, NULL); YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides); - if (encoder == NULL) { - return JNI_FALSE; + jboolean result = JNI_FALSE; + if (encoder != NULL) { + encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality); + delete encoder; + result = JNI_TRUE; } - encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality); - delete encoder; env->ReleaseByteArrayElements(inYuv, yuv, 0); env->ReleaseIntArrayElements(offsets, imgOffsets, 0); env->ReleaseIntArrayElements(strides, imgStrides, 0); - return JNI_TRUE; + return result; } /////////////////////////////////////////////////////////////////////////////// diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index 67f3879..af6cc72 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "CursorWindow" +#include <inttypes.h> #include <jni.h> #include <JNIHelp.h> #include <android_runtime/AndroidRuntime.h> @@ -225,7 +226,7 @@ static jstring nativeGetString(JNIEnv* env, jclass clazz, jlong windowPtr, } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { int64_t value = window->getFieldSlotValueLong(fieldSlot); char buf[32]; - snprintf(buf, sizeof(buf), "%lld", value); + snprintf(buf, sizeof(buf), "%" PRId64, value); return env->NewStringUTF(buf); } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { double value = window->getFieldSlotValueDouble(fieldSlot); @@ -314,7 +315,7 @@ static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jlong windowPtr, } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { int64_t value = window->getFieldSlotValueLong(fieldSlot); char buf[32]; - snprintf(buf, sizeof(buf), "%lld", value); + snprintf(buf, sizeof(buf), "%" PRId64, value); fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { double value = window->getFieldSlotValueDouble(fieldSlot); diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp index f904b62..031637f 100644 --- a/core/jni/android_net_TrafficStats.cpp +++ b/core/jni/android_net_TrafficStats.cpp @@ -19,6 +19,7 @@ #include <dirent.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <sys/stat.h> #include <sys/types.h> @@ -85,9 +86,9 @@ static int parseIfaceStats(const char* iface, struct Stats* stats) { uint64_t rxBytes, rxPackets, txBytes, txPackets, tcpRxPackets, tcpTxPackets; while (fgets(buffer, sizeof(buffer), fp) != NULL) { - int matched = sscanf(buffer, "%31s %llu %llu %llu %llu " - "%*u %llu %*u %*u %*u %*u " - "%*u %llu %*u %*u %*u %*u", cur_iface, &rxBytes, + int matched = sscanf(buffer, "%31s %" SCNu64 " %" SCNu64 " %" SCNu64 + " %" SCNu64 " " "%*u %" SCNu64 " %*u %*u %*u %*u " + "%*u %" SCNu64 " %*u %*u %*u %*u", cur_iface, &rxBytes, &rxPackets, &txBytes, &txPackets, &tcpRxPackets, &tcpTxPackets); if (matched >= 5) { if (matched == 7) { @@ -129,9 +130,11 @@ static int parseUidStats(const uint32_t uid, struct Stats* stats) { uint64_t tag, rxBytes, rxPackets, txBytes, txPackets; while (fgets(buffer, sizeof(buffer), fp) != NULL) { - if (sscanf(buffer, "%d %31s 0x%llx %u %u %llu %llu %llu %llu", &idx, - iface, &tag, &cur_uid, &set, &rxBytes, &rxPackets, &txBytes, - &txPackets) == 9) { + if (sscanf(buffer, + "%" SCNu32 " %31s 0x%" SCNx64 " %u %u %" SCNu64 " %" SCNu64 + " %" SCNu64 " %" SCNu64 "", + &idx, iface, &tag, &cur_uid, &set, &rxBytes, &rxPackets, + &txBytes, &txPackets) == 9) { if (uid == cur_uid && tag == 0L) { stats->rxBytes += rxBytes; stats->rxPackets += rxPackets; diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index d4873d6..86207f0 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -24,6 +24,7 @@ #include <cutils/log.h> #include <fcntl.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -824,9 +825,9 @@ static void dumpNativeHeap(FILE* fp) break; } else { #ifdef __LP64__ - fprintf(fp, " %016x", backtrace[bt]); + fprintf(fp, " %016" PRIxPTR, backtrace[bt]); #else - fprintf(fp, " %08x", backtrace[bt]); + fprintf(fp, " %08" PRIxPTR, backtrace[bt]); #endif } } diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 475e926..662af89 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -23,6 +23,7 @@ #include "JNIHelp.h" #include <fcntl.h> +#include <inttypes.h> #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> @@ -334,7 +335,7 @@ public: if (b == NULL) { b = new JavaBBinder(env, obj); mBinder = b; - ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%d\n", + ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n", b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount()); } @@ -697,9 +698,9 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, "Not allowed to write file descriptors here"); break; default: - ALOGE("Unknown binder error code. 0x%x", err); + ALOGE("Unknown binder error code. 0x%" PRIx32, err); String8 msg; - msg.appendFormat("Unknown binder error code. 0x%x", err); + msg.appendFormat("Unknown binder error code. 0x%" PRIx32, err); // RemoteException is a checked exception, only throw from certain methods. jniThrowException(env, canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException", msg.string()); @@ -733,7 +734,7 @@ static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, if (uid > 0 && uid < 999) { // In Android currently there are no uids in this range. char buf[128]; - sprintf(buf, "Restoring bad calling ident: 0x%Lx", token); + sprintf(buf, "Restoring bad calling ident: 0x%" PRIx64, token); jniThrowException(env, "java/lang/IllegalStateException", buf); return; } @@ -965,8 +966,8 @@ static bool push_eventlog_string(char** pos, const char* end, const char* str) { jint len = strlen(str); int space_needed = 1 + sizeof(len) + len; if (end - *pos < space_needed) { - ALOGW("not enough space for string. remain=%d; needed=%d", - (end - *pos), space_needed); + ALOGW("not enough space for string. remain=%" PRIdPTR "; needed=%d", + end - *pos, space_needed); return false; } **pos = EVENT_TYPE_STRING; @@ -981,8 +982,8 @@ static bool push_eventlog_string(char** pos, const char* end, const char* str) { static bool push_eventlog_int(char** pos, const char* end, jint val) { int space_needed = 1 + sizeof(val); if (end - *pos < space_needed) { - ALOGW("not enough space for int. remain=%d; needed=%d", - (end - *pos), space_needed); + ALOGW("not enough space for int. remain=%" PRIdPTR "; needed=%d", + end - *pos, space_needed); return false; } **pos = EVENT_TYPE_INT; @@ -1068,7 +1069,7 @@ static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, return JNI_FALSE; } - ALOGV("Java code calling transact on %p in Java object %p with code %d\n", + ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n", target, obj, code); #if ENABLE_BINDER_SAMPLE diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp index 2593420..8a0eaa2 100644 --- a/core/jni/android_util_EventLog.cpp +++ b/core/jni/android_util_EventLog.cpp @@ -177,13 +177,13 @@ static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED, break; } if (ret < 0) { - if (errno == EINTR) { + if (ret == -EINTR) { continue; } - if (errno == EINVAL) { + if (ret == -EINVAL) { jniThrowException(env, "java/io/IOException", "Event too short"); - } else if (errno != EAGAIN) { - jniThrowIOException(env, errno); // Will throw on return + } else if (ret != -EAGAIN) { + jniThrowIOException(env, -ret); // Will throw on return } break; } diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 601975a..31876ce 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -19,8 +19,8 @@ #include <utils/Log.h> #include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> #include <binder/IServiceManager.h> +#include <cutils/process_name.h> #include <cutils/sched_policy.h> #include <utils/String8.h> #include <utils/Vector.h> @@ -30,15 +30,16 @@ #include "android_util_Binder.h" #include "JNIHelp.h" -#include <sys/errno.h> -#include <sys/resource.h> -#include <sys/types.h> -#include <sys/stat.h> #include <dirent.h> #include <fcntl.h> #include <grp.h> +#include <inttypes.h> #include <pwd.h> #include <signal.h> +#include <sys/errno.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> #define POLICY_DEBUG 0 @@ -159,7 +160,7 @@ jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name) void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int tid, jint grp) { - ALOGV("%s tid=%d grp=%d", __func__, tid, grp); + ALOGV("%s tid=%d grp=%" PRId32, __func__, tid, grp); SchedPolicy sp = (SchedPolicy) grp; int res = set_sched_policy(tid, sp); if (res != NO_ERROR) { @@ -169,7 +170,7 @@ void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int tid, jint void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp) { - ALOGV("%s pid=%d grp=%d", __func__, pid, grp); + ALOGV("%s pid=%d grp=%" PRId32, __func__, pid, grp); DIR *d; FILE *fp; char proc_path[255]; @@ -322,7 +323,7 @@ void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz, } } - //ALOGI("Setting priority of %d: %d, getpriority returns %d\n", + //ALOGI("Setting priority of %" PRId32 ": %" PRId32 ", getpriority returns %d\n", // pid, pri, getpriority(PRIO_PROCESS, pid)); } @@ -340,7 +341,7 @@ jint android_os_Process_getThreadPriority(JNIEnv* env, jobject clazz, if (errno != 0) { signalExceptionForPriorityError(env, errno); } - //ALOGI("Returning priority of %d: %d\n", pid, pri); + //ALOGI("Returning priority of %" PRId32 ": %" PRId32 "\n", pid, pri); return pri; } @@ -379,7 +380,7 @@ jboolean android_os_Process_setSwappiness(JNIEnv *env, jobject clazz, int fd = open(text, O_WRONLY); if (fd >= 0) { - sprintf(text, "%d", pid); + sprintf(text, "%" PRId32, pid); write(fd, text, strlen(text)); close(fd); } @@ -402,7 +403,9 @@ void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name) } if (name8.size() > 0) { - ProcessState::self()->setArgV0(name8.string()); + const char* procName = name8.string(); + set_process_name(procName); + AndroidRuntime::getRuntime()->setArgv0(procName); } } @@ -418,7 +421,7 @@ jint android_os_Process_setGid(JNIEnv* env, jobject clazz, jint uid) static int pid_compare(const void* v1, const void* v2) { - //ALOGI("Compare %d vs %d\n", *((const jint*)v1), *((const jint*)v2)); + //ALOGI("Compare %" PRId32 " vs %" PRId32 "\n", *((const jint*)v1), *((const jint*)v2)); return *((const jint*)v1) - *((const jint*)v2); } @@ -532,7 +535,7 @@ void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileSt return; } - //ALOGI("Clearing %d sizes", count); + //ALOGI("Clearing %" PRId32 " sizes", count); for (i=0; i<count; i++) { sizesArray[i] = 0; } @@ -571,7 +574,7 @@ void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileSt } char* end; sizesArray[i] = strtoll(num, &end, 10); - //ALOGI("Field %s = %d", field.string(), sizesArray[i]); + //ALOGI("Field %s = %" PRId64, field.string(), sizesArray[i]); foundCount++; break; } @@ -773,7 +776,7 @@ jboolean android_os_Process_parseProcLineArray(JNIEnv* env, jobject clazz, } } - //ALOGI("Field %d: %d-%d dest=%d mode=0x%x\n", i, start, end, di, mode); + //ALOGI("Field %" PRId32 ": %" PRId32 "-%" PRId32 " dest=%" PRId32 " mode=0x%" PRIx32 "\n", i, start, end, di, mode); if ((mode&(PROC_OUT_FLOAT|PROC_OUT_LONG|PROC_OUT_STRING)) != 0) { char c = buffer[end]; @@ -872,7 +875,7 @@ void android_os_Process_setApplicationObject(JNIEnv* env, jobject clazz, void android_os_Process_sendSignal(JNIEnv* env, jobject clazz, jint pid, jint sig) { if (pid > 0) { - ALOGI("Sending signal. PID: %d SIG: %d", pid, sig); + ALOGI("Sending signal. PID: %" PRId32 " SIG: %" PRId32, pid, sig); kill(pid, sig); } } @@ -902,7 +905,7 @@ static jlong android_os_Process_getPss(JNIEnv* env, jobject clazz, jint pid) { char filename[64]; - snprintf(filename, sizeof(filename), "/proc/%d/smaps", pid); + snprintf(filename, sizeof(filename), "/proc/%" PRId32 "/smaps", pid); FILE * file = fopen(filename, "r"); if (!file) { @@ -914,7 +917,7 @@ static jlong android_os_Process_getPss(JNIEnv* env, jobject clazz, jint pid) jlong pss = 0; while (fgets(line, sizeof(line), file)) { jlong v; - if (sscanf(line, "Pss: %lld kB", &v) == 1) { + if (sscanf(line, "Pss: %" SCNd64 " kB", &v) == 1) { pss += v; } } diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index a860918..2004576 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -19,11 +19,12 @@ #include <android_runtime/AndroidRuntime.h> -#include <utils/Log.h> -#include <androidfw/ZipFileRO.h> -#include <androidfw/ZipUtils.h> #include <ScopedUtfChars.h> #include <UniquePtr.h> +#include <androidfw/ZipFileRO.h> +#include <androidfw/ZipUtils.h> +#include <utils/Log.h> +#include <utils/Vector.h> #include <zlib.h> @@ -54,17 +55,19 @@ namespace android { // These match PackageManager.java install codes -typedef enum { +enum install_status_t { INSTALL_SUCCEEDED = 1, INSTALL_FAILED_INVALID_APK = -2, INSTALL_FAILED_INSUFFICIENT_STORAGE = -4, INSTALL_FAILED_CONTAINER_ERROR = -18, INSTALL_FAILED_INTERNAL_ERROR = -110, -} install_status_t; + INSTALL_FAILED_NO_MATCHING_ABIS = -112, + NO_NATIVE_LIBRARIES = -113 +}; typedef install_status_t (*iterFunc)(JNIEnv*, void*, ZipFileRO*, ZipEntryRO, const char*); -// Equivalent to isFilenameSafe +// Equivalent to android.os.FileUtils.isFilenameSafe static bool isFilenameSafe(const char* filename) { @@ -268,126 +271,252 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr return INSTALL_SUCCEEDED; } -static install_status_t -iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2, - iterFunc callFunc, void* callArg) { - ScopedUtfChars filePath(env, javaFilePath); - ScopedUtfChars cpuAbi(env, javaCpuAbi); - ScopedUtfChars cpuAbi2(env, javaCpuAbi2); - - UniquePtr<ZipFileRO> zipFile(ZipFileRO::open(filePath.c_str())); - if (zipFile.get() == NULL) { - ALOGI("Couldn't open APK %s\n", filePath.c_str()); - return INSTALL_FAILED_INVALID_APK; +/* + * An iterator over all shared libraries in a zip file. An entry is + * considered to be a shared library if all of the conditions below are + * satisfied : + * + * - The entry is under the lib/ directory. + * - The entry name ends with ".so" and the entry name starts with "lib", + * an exception is made for entries whose name is "gdbserver". + * - The entry filename is "safe" (as determined by isFilenameSafe). + * + */ +class NativeLibrariesIterator { +private: + NativeLibrariesIterator(ZipFileRO* zipFile, void* cookie) + : mZipFile(zipFile), mCookie(cookie), mLastSlash(NULL) { + fileName[0] = '\0'; } - char fileName[PATH_MAX]; - bool hasPrimaryAbi = false; +public: + static NativeLibrariesIterator* create(ZipFileRO* zipFile) { + void* cookie = NULL; + if (!zipFile->startIteration(&cookie)) { + return NULL; + } - void* cookie = NULL; - if (!zipFile->startIteration(&cookie)) { - ALOGI("Couldn't iterate over APK%s\n", filePath.c_str()); - return INSTALL_FAILED_INVALID_APK; + return new NativeLibrariesIterator(zipFile, cookie); } - ZipEntryRO entry = NULL; - while ((entry = zipFile->nextEntry(cookie)) != NULL) { - // Make sure this entry has a filename. - if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) { - continue; - } + ZipEntryRO next() { + ZipEntryRO next = NULL; + while ((next = mZipFile->nextEntry(mCookie)) != NULL) { + // Make sure this entry has a filename. + if (mZipFile->getEntryFileName(next, fileName, sizeof(fileName))) { + continue; + } - // Make sure we're in the lib directory of the ZIP. - if (strncmp(fileName, APK_LIB, APK_LIB_LEN)) { - continue; - } + // Make sure we're in the lib directory of the ZIP. + if (strncmp(fileName, APK_LIB, APK_LIB_LEN)) { + continue; + } - // Make sure the filename is at least to the minimum library name size. - const size_t fileNameLen = strlen(fileName); - static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN; - if (fileNameLen < minLength) { - continue; - } + // Make sure the filename is at least to the minimum library name size. + const size_t fileNameLen = strlen(fileName); + static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN; + if (fileNameLen < minLength) { + continue; + } - const char* lastSlash = strrchr(fileName, '/'); - ALOG_ASSERT(lastSlash != NULL, "last slash was null somehow for %s\n", fileName); + const char* lastSlash = strrchr(fileName, '/'); + ALOG_ASSERT(lastSlash != NULL, "last slash was null somehow for %s\n", fileName); - // Check to make sure the CPU ABI of this file is one we support. - const char* cpuAbiOffset = fileName + APK_LIB_LEN; - const size_t cpuAbiRegionSize = lastSlash - cpuAbiOffset; + // Exception: If we find the gdbserver binary, return it. + if (!strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) { + break; + } - ALOGV("Comparing ABIs %s and %s versus %s\n", cpuAbi.c_str(), cpuAbi2.c_str(), cpuAbiOffset); - if (cpuAbi.size() == cpuAbiRegionSize - && *(cpuAbiOffset + cpuAbi.size()) == '/' - && !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) { - ALOGV("Using primary ABI %s\n", cpuAbi.c_str()); - hasPrimaryAbi = true; - } else if (cpuAbi2.size() == cpuAbiRegionSize - && *(cpuAbiOffset + cpuAbi2.size()) == '/' - && !strncmp(cpuAbiOffset, cpuAbi2.c_str(), cpuAbiRegionSize)) { - - /* - * If this library matches both the primary and secondary ABIs, - * only use the primary ABI. - */ - if (hasPrimaryAbi) { - ALOGV("Already saw primary ABI, skipping secondary ABI %s\n", cpuAbi2.c_str()); + // Make sure the filename starts with lib and ends with ".so". + if (strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN) + || strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN)) { continue; - } else { - ALOGV("Using secondary ABI %s\n", cpuAbi2.c_str()); } - } else { - ALOGV("abi didn't match anything: %s (end at %zd)\n", cpuAbiOffset, cpuAbiRegionSize); - continue; + + // Make sure the filename is safe. + if (!isFilenameSafe(lastSlash + 1)) { + continue; + } + + mLastSlash = lastSlash; + break; } - // If this is a .so file, check to see if we need to copy it. - if ((!strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN) - && !strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN) - && isFilenameSafe(lastSlash + 1)) - || !strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) { + return next; + } + + inline const char* currentEntry() const { + return fileName; + } + + inline const char* lastSlash() const { + return mLastSlash; + } + + virtual ~NativeLibrariesIterator() { + mZipFile->endIteration(mCookie); + } +private: + + char fileName[PATH_MAX]; + ZipFileRO* const mZipFile; + void* mCookie; + const char* mLastSlash; +}; - install_status_t ret = callFunc(env, callArg, zipFile.get(), entry, lastSlash + 1); +static install_status_t +iterateOverNativeFiles(JNIEnv *env, jlong apkHandle, jstring javaCpuAbi, + iterFunc callFunc, void* callArg) { + ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle); + if (zipFile == NULL) { + return INSTALL_FAILED_INVALID_APK; + } + + UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile)); + if (it.get() == NULL) { + return INSTALL_FAILED_INVALID_APK; + } + + const ScopedUtfChars cpuAbi(env, javaCpuAbi); + if (cpuAbi.c_str() == NULL) { + // This would've thrown, so this return code isn't observable by + // Java. + return INSTALL_FAILED_INVALID_APK; + } + ZipEntryRO entry = NULL; + while ((entry = it->next()) != NULL) { + const char* fileName = it->currentEntry(); + const char* lastSlash = it->lastSlash(); + + // Check to make sure the CPU ABI of this file is one we support. + const char* cpuAbiOffset = fileName + APK_LIB_LEN; + const size_t cpuAbiRegionSize = lastSlash - cpuAbiOffset; + + if (cpuAbi.size() == cpuAbiRegionSize && !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) { + install_status_t ret = callFunc(env, callArg, zipFile, entry, lastSlash + 1); if (ret != INSTALL_SUCCEEDED) { ALOGV("Failure for entry %s", lastSlash + 1); - zipFile->endIteration(cookie); return ret; } } } - zipFile->endIteration(cookie); - return INSTALL_SUCCEEDED; } + +static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray) { + const int numAbis = env->GetArrayLength(supportedAbisArray); + Vector<ScopedUtfChars*> supportedAbis; + + for (int i = 0; i < numAbis; ++i) { + supportedAbis.add(new ScopedUtfChars(env, + (jstring) env->GetObjectArrayElement(supportedAbisArray, i))); + } + + ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle); + if (zipFile == NULL) { + return INSTALL_FAILED_INVALID_APK; + } + + UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile)); + if (it.get() == NULL) { + return INSTALL_FAILED_INVALID_APK; + } + + ZipEntryRO entry = NULL; + char fileName[PATH_MAX]; + int status = NO_NATIVE_LIBRARIES; + while ((entry = it->next()) != NULL) { + // We're currently in the lib/ directory of the APK, so it does have some native + // code. We should return INSTALL_FAILED_NO_MATCHING_ABIS if none of the + // libraries match. + if (status == NO_NATIVE_LIBRARIES) { + status = INSTALL_FAILED_NO_MATCHING_ABIS; + } + + const char* fileName = it->currentEntry(); + const char* lastSlash = it->lastSlash(); + + // Check to see if this CPU ABI matches what we are looking for. + const char* abiOffset = fileName + APK_LIB_LEN; + const size_t abiSize = lastSlash - abiOffset; + for (int i = 0; i < numAbis; i++) { + const ScopedUtfChars* abi = supportedAbis[i]; + if (abi->size() == abiSize && !strncmp(abiOffset, abi->c_str(), abiSize)) { + // The entry that comes in first (i.e. with a lower index) has the higher priority. + if (((i < status) && (status >= 0)) || (status < 0) ) { + status = i; + } + } + } + } + + for (int i = 0; i < numAbis; ++i) { + delete supportedAbis[i]; + } + + return status; +} + static jint com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz, - jstring javaFilePath, jstring javaNativeLibPath, jstring javaCpuAbi, jstring javaCpuAbi2) + jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi) { - return (jint) iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2, + return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi, copyFileIfChanged, &javaNativeLibPath); } static jlong com_android_internal_content_NativeLibraryHelper_sumNativeBinaries(JNIEnv *env, jclass clazz, - jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2) + jlong apkHandle, jstring javaCpuAbi) { size_t totalSize = 0; - iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2, sumFiles, &totalSize); + iterateOverNativeFiles(env, apkHandle, javaCpuAbi, sumFiles, &totalSize); return totalSize; } +static jint +com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, jclass clazz, + jlong apkHandle, jobjectArray javaCpuAbisToSearch) +{ + return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch); +} + +static jlong +com_android_internal_content_NativeLibraryHelper_openApk(JNIEnv *env, jclass, jstring apkPath) +{ + ScopedUtfChars filePath(env, apkPath); + ZipFileRO* zipFile = ZipFileRO::open(filePath.c_str()); + + return reinterpret_cast<jlong>(zipFile); +} + +static void +com_android_internal_content_NativeLibraryHelper_close(JNIEnv *env, jclass, jlong apkHandle) +{ + delete reinterpret_cast<ZipFileRO*>(apkHandle); +} + static JNINativeMethod gMethods[] = { + {"nativeOpenApk", + "(Ljava/lang/String;)J", + (void *)com_android_internal_content_NativeLibraryHelper_openApk}, + {"nativeClose", + "(J)V", + (void *)com_android_internal_content_NativeLibraryHelper_close}, {"nativeCopyNativeBinaries", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + "(JLjava/lang/String;Ljava/lang/String;)I", (void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries}, {"nativeSumNativeBinaries", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J", + "(JLjava/lang/String;)J", (void *)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries}, + {"nativeFindSupportedAbi", + "(J[Ljava/lang/String;)I", + (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi}, }; diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp new file mode 100644 index 0000000..c58bf04 --- /dev/null +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -0,0 +1,606 @@ +/* + * Copyright (C) 2008 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. + */ + +#include "android_runtime/AndroidRuntime.h" + +// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc +#include <sys/mount.h> +#include <linux/fs.h> + +#include <grp.h> +#include <paths.h> +#include <signal.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> +#include <fcntl.h> + +#include "cutils/fs.h" +#include "cutils/multiuser.h" +#include "cutils/sched_policy.h" +#include "utils/String8.h" +#include "JNIHelp.h" +#include "ScopedLocalRef.h" +#include "ScopedPrimitiveArray.h" +#include "ScopedUtfChars.h" + +#if defined(HAVE_PRCTL) +#include <sys/prctl.h> +#endif + +#include <selinux/android.h> + +#if defined(__linux__) +#include <sys/personality.h> +#include <sys/utsname.h> +#if defined(HAVE_ANDROID_OS) +#include <sys/capability.h> +#endif +#endif + +namespace { + +using android::String8; + +static pid_t gSystemServerPid = 0; + +static const char kZygoteClassName[] = "com/android/internal/os/Zygote"; +static jclass gZygoteClass; +static jmethodID gCallPostForkChildHooks; + +// Must match values in com.android.internal.os.Zygote. +enum MountExternalKind { + MOUNT_EXTERNAL_NONE = 0, + MOUNT_EXTERNAL_SINGLEUSER = 1, + MOUNT_EXTERNAL_MULTIUSER = 2, + MOUNT_EXTERNAL_MULTIUSER_ALL = 3, +}; + +static void RuntimeAbort(JNIEnv* env) { + env->FatalError("RuntimeAbort"); +} + +// This signal handler is for zygote mode, since the zygote must reap its children +static void SigChldHandler(int /*signal_number*/) { + pid_t pid; + int status; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + // Log process-death status that we care about. In general it is + // not safe to call LOG(...) from a signal handler because of + // possible reentrancy. However, we know a priori that the + // current implementation of LOG() is safe to call from a SIGCHLD + // handler in the zygote process. If the LOG() implementation + // changes its locking strategy or its use of syscalls within the + // lazy-init critical section, its use here may become unsafe. + if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) { + ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status)); + } + } else if (WIFSIGNALED(status)) { + if (WTERMSIG(status) != SIGKILL) { + ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status)); + } +#ifdef WCOREDUMP + if (WCOREDUMP(status)) { + ALOGI("Process %d dumped core.", pid); + } +#endif /* ifdef WCOREDUMP */ + } + + // If the just-crashed process is the system_server, bring down zygote + // so that it is restarted by init and system server will be restarted + // from there. + if (pid == gSystemServerPid) { + ALOGE("Exit zygote because system server (%d) has terminated"); + kill(getpid(), SIGKILL); + } + } + + // Note that we shouldn't consider ECHILD an error because + // the secondary zygote might have no children left to wait for. + if (pid < 0 && errno != ECHILD) { + ALOGW("Zygote SIGCHLD error in waitpid: %s", strerror(errno)); + } +} + +// Configures the SIGCHLD handler for the zygote process. This is configured +// very late, because earlier in the runtime we may fork() and exec() +// other processes, and we want to waitpid() for those rather than +// have them be harvested immediately. +// +// This ends up being called repeatedly before each fork(), but there's +// no real harm in that. +static void SetSigChldHandler() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SigChldHandler; + + int err = sigaction(SIGCHLD, &sa, NULL); + if (err < 0) { + ALOGW("Error setting SIGCHLD handler: %d", errno); + } +} + +// Sets the SIGCHLD handler back to default behavior in zygote children. +static void UnsetSigChldHandler() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + + int err = sigaction(SIGCHLD, &sa, NULL); + if (err < 0) { + ALOGW("Error unsetting SIGCHLD handler: %d", errno); + } +} + +// Calls POSIX setgroups() using the int[] object as an argument. +// A NULL argument is tolerated. +static void SetGids(JNIEnv* env, jintArray javaGids) { + if (javaGids == NULL) { + return; + } + + ScopedIntArrayRO gids(env, javaGids); + if (gids.get() == NULL) { + RuntimeAbort(env); + } + int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0])); + if (rc == -1) { + ALOGE("setgroups failed"); + RuntimeAbort(env); + } +} + +// Sets the resource limits via setrlimit(2) for the values in the +// two-dimensional array of integers that's passed in. The second dimension +// contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is +// treated as an empty array. +static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) { + if (javaRlimits == NULL) { + return; + } + + rlimit rlim; + memset(&rlim, 0, sizeof(rlim)); + + for (int i = 0; i < env->GetArrayLength(javaRlimits); ++i) { + ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i)); + ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get())); + if (javaRlimit.size() != 3) { + ALOGE("rlimits array must have a second dimension of size 3"); + RuntimeAbort(env); + } + + rlim.rlim_cur = javaRlimit[1]; + rlim.rlim_max = javaRlimit[2]; + + int rc = setrlimit(javaRlimit[0], &rlim); + if (rc == -1) { + ALOGE("setrlimit(%d, {%d, %d}) failed", javaRlimit[0], rlim.rlim_cur, rlim.rlim_max); + RuntimeAbort(env); + } + } +} + +#if defined(HAVE_ANDROID_OS) + +// The debug malloc library needs to know whether it's the zygote or a child. +extern "C" int gMallocLeakZygoteChild; + +static void EnableKeepCapabilities(JNIEnv* env) { + int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + if (rc == -1) { + ALOGE("prctl(PR_SET_KEEPCAPS) failed"); + RuntimeAbort(env); + } +} + +static void DropCapabilitiesBoundingSet(JNIEnv* env) { + for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) { + int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0); + if (rc == -1) { + if (errno == EINVAL) { + ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify " + "your kernel is compiled with file capabilities support"); + } else { + ALOGE("prctl(PR_CAPBSET_DROP) failed"); + RuntimeAbort(env); + } + } + } +} + +static void SetCapabilities(JNIEnv* env, int64_t permitted, int64_t effective) { + __user_cap_header_struct capheader; + memset(&capheader, 0, sizeof(capheader)); + capheader.version = _LINUX_CAPABILITY_VERSION_3; + capheader.pid = 0; + + __user_cap_data_struct capdata[2]; + memset(&capdata, 0, sizeof(capdata)); + capdata[0].effective = effective; + capdata[1].effective = effective >> 32; + capdata[0].permitted = permitted; + capdata[1].permitted = permitted >> 32; + + if (capset(&capheader, &capdata[0]) == -1) { + ALOGE("capset(%lld, %lld) failed", permitted, effective); + RuntimeAbort(env); + } +} + +static void SetSchedulerPolicy(JNIEnv* env) { + errno = -set_sched_policy(0, SP_DEFAULT); + if (errno != 0) { + ALOGE("set_sched_policy(0, SP_DEFAULT) failed"); + RuntimeAbort(env); + } +} + +#else + +static int gMallocLeakZygoteChild = 0; + +static void EnableKeepCapabilities(JNIEnv*) {} +static void DropCapabilitiesBoundingSet(JNIEnv*) {} +static void SetCapabilities(JNIEnv*, int64_t, int64_t) {} +static void SetSchedulerPolicy(JNIEnv*) {} + +#endif + +// Create a private mount namespace and bind mount appropriate emulated +// storage for the given user. +static bool MountEmulatedStorage(uid_t uid, jint mount_mode) { + if (mount_mode == MOUNT_EXTERNAL_NONE) { + return true; + } + + // See storage config details at http://source.android.com/tech/storage/ + userid_t user_id = multiuser_get_user_id(uid); + + // Create a second private mount namespace for our process + if (unshare(CLONE_NEWNS) == -1) { + ALOGW("Failed to unshare(): %d", errno); + return false; + } + + // Create bind mounts to expose external storage + if (mount_mode == MOUNT_EXTERNAL_MULTIUSER || mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) { + // These paths must already be created by init.rc + const char* source = getenv("EMULATED_STORAGE_SOURCE"); + const char* target = getenv("EMULATED_STORAGE_TARGET"); + const char* legacy = getenv("EXTERNAL_STORAGE"); + if (source == NULL || target == NULL || legacy == NULL) { + ALOGW("Storage environment undefined; unable to provide external storage"); + return false; + } + + // Prepare source paths + + // /mnt/shell/emulated/0 + const String8 source_user(String8::format("%s/%d", source, user_id)); + // /storage/emulated/0 + const String8 target_user(String8::format("%s/%d", target, user_id)); + + if (fs_prepare_dir(source_user.string(), 0000, 0, 0) == -1 + || fs_prepare_dir(target_user.string(), 0000, 0, 0) == -1) { + return false; + } + + if (mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) { + // Mount entire external storage tree for all users + if (TEMP_FAILURE_RETRY(mount(source, target, NULL, MS_BIND, NULL)) == -1) { + ALOGW("Failed to mount %s to %s :%d", source, target, errno); + return false; + } + } else { + // Only mount user-specific external storage + if (TEMP_FAILURE_RETRY( + mount(source_user.string(), target_user.string(), NULL, MS_BIND, NULL)) == -1) { + ALOGW("Failed to mount %s to %s: %d", source_user.string(), target_user.string(), errno); + return false; + } + } + + if (fs_prepare_dir(legacy, 0000, 0, 0) == -1) { + return false; + } + + // Finally, mount user-specific path into place for legacy users + if (TEMP_FAILURE_RETRY( + mount(target_user.string(), legacy, NULL, MS_BIND | MS_REC, NULL)) == -1) { + ALOGW("Failed to mount %s to %s: %d", target_user.string(), legacy, errno); + return false; + } + } else { + ALOGW("Mount mode %d unsupported", mount_mode); + return false; + } + + return true; +} + +#if defined(__linux__) +static bool NeedsNoRandomizeWorkaround() { +#if !defined(__arm__) + return false; +#else + int major; + int minor; + struct utsname uts; + if (uname(&uts) == -1) { + return false; + } + + if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) { + return false; + } + + // Kernels before 3.4.* need the workaround. + return (major < 3) || ((major == 3) && (minor < 4)); +#endif +} +#endif + +// Utility to close down the Zygote socket file descriptors while +// the child is still running as root with Zygote's privileges. Each +// descriptor (if any) is closed via dup2(), replacing it with a valid +// (open) descriptor to /dev/null. + +static void DetachDescriptors(JNIEnv* env, jintArray fdsToClose) { + if (!fdsToClose) { + return; + } + jsize count = env->GetArrayLength(fdsToClose); + jint *ar = env->GetIntArrayElements(fdsToClose, 0); + if (!ar) { + ALOGE("Bad fd array"); + RuntimeAbort(env); + } + jsize i; + int devnull; + for (i = 0; i < count; i++) { + devnull = open("/dev/null", O_RDWR); + if (devnull < 0) { + ALOGE("Failed to open /dev/null"); + RuntimeAbort(env); + continue; + } + ALOGV("Switching descriptor %d to /dev/null: %d", ar[i], errno); + if (dup2(devnull, ar[i]) < 0) { + ALOGE("Failed dup2() on descriptor %d", ar[i]); + RuntimeAbort(env); + } + close(devnull); + } +} + +void SetThreadName(const char* thread_name) { + bool hasAt = false; + bool hasDot = false; + const char* s = thread_name; + while (*s) { + if (*s == '.') { + hasDot = true; + } else if (*s == '@') { + hasAt = true; + } + s++; + } + const int len = s - thread_name; + if (len < 15 || hasAt || !hasDot) { + s = thread_name; + } else { + s = thread_name + len - 15; + } + // pthread_setname_np fails rather than truncating long strings. + char buf[16]; // MAX_TASK_COMM_LEN=16 is hard-coded into bionic + strlcpy(buf, s, sizeof(buf)-1); + errno = pthread_setname_np(pthread_self(), buf); + if (errno != 0) { + ALOGW("Unable to set the name of current thread to '%s'", buf); + } +} + +// Utility routine to fork zygote and specialize the child process. +static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids, + jint debug_flags, jobjectArray javaRlimits, + jlong permittedCapabilities, jlong effectiveCapabilities, + jint mount_external, + jstring java_se_info, jstring java_se_name, + bool is_system_server, jintArray fdsToClose) { + SetSigChldHandler(); + + pid_t pid = fork(); + + if (pid == 0) { + // The child process. + gMallocLeakZygoteChild = 1; + + // Clean up any descriptors which must be closed immediately + DetachDescriptors(env, fdsToClose); + + // Keep capabilities across UID change, unless we're staying root. + if (uid != 0) { + EnableKeepCapabilities(env); + } + + DropCapabilitiesBoundingSet(env); + + if (!MountEmulatedStorage(uid, mount_external)) { + ALOGW("Failed to mount emulated storage: %d", errno); + if (errno == ENOTCONN || errno == EROFS) { + // When device is actively encrypting, we get ENOTCONN here + // since FUSE was mounted before the framework restarted. + // When encrypted device is booting, we get EROFS since + // FUSE hasn't been created yet by init. + // In either case, continue without external storage. + } else { + ALOGE("Cannot continue without emulated storage"); + RuntimeAbort(env); + } + } + + SetGids(env, javaGids); + + SetRLimits(env, javaRlimits); + + int rc = setresgid(gid, gid, gid); + if (rc == -1) { + ALOGE("setresgid(%d) failed", gid); + RuntimeAbort(env); + } + + rc = setresuid(uid, uid, uid); + if (rc == -1) { + ALOGE("setresuid(%d) failed", uid); + RuntimeAbort(env); + } + +#if defined(__linux__) + if (NeedsNoRandomizeWorkaround()) { + // Work around ARM kernel ASLR lossage (http://b/5817320). + int old_personality = personality(0xffffffff); + int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE); + if (new_personality == -1) { + ALOGW("personality(%d) failed", new_personality); + } + } +#endif + + SetCapabilities(env, permittedCapabilities, effectiveCapabilities); + + SetSchedulerPolicy(env); + +#if defined(HAVE_ANDROID_OS) + { // NOLINT(whitespace/braces) + const char* se_info_c_str = NULL; + ScopedUtfChars* se_info = NULL; + if (java_se_info != NULL) { + se_info = new ScopedUtfChars(env, java_se_info); + se_info_c_str = se_info->c_str(); + if (se_info_c_str == NULL) { + ALOGE("se_info_c_str == NULL"); + RuntimeAbort(env); + } + } + const char* se_name_c_str = NULL; + ScopedUtfChars* se_name = NULL; + if (java_se_name != NULL) { + se_name = new ScopedUtfChars(env, java_se_name); + se_name_c_str = se_name->c_str(); + if (se_name_c_str == NULL) { + ALOGE("se_name_c_str == NULL"); + RuntimeAbort(env); + } + } + rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str); + if (rc == -1) { + ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid, + is_system_server, se_info_c_str, se_name_c_str); + RuntimeAbort(env); + } + + // Make it easier to debug audit logs by setting the main thread's name to the + // nice name rather than "app_process". + if (se_info_c_str == NULL && is_system_server) { + se_name_c_str = "system_server"; + } + if (se_info_c_str != NULL) { + SetThreadName(se_name_c_str); + } + + delete se_info; + delete se_name; + } +#else + UNUSED(is_system_server); + UNUSED(java_se_info); + UNUSED(java_se_name); +#endif + + UnsetSigChldHandler(); + + env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags); + if (env->ExceptionCheck()) { + ALOGE("Error calling post fork hooks."); + RuntimeAbort(env); + } + } else if (pid > 0) { + // the parent process + } + return pid; +} +} // anonymous namespace + +namespace android { + +static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( + JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, + jint debug_flags, jobjectArray rlimits, + jint mount_external, jstring se_info, jstring se_name, + jintArray fdsToClose) { + return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, + rlimits, 0, 0, mount_external, se_info, se_name, false, fdsToClose); +} + +static jint com_android_internal_os_Zygote_nativeForkSystemServer( + JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids, + jint debug_flags, jobjectArray rlimits, jlong permittedCapabilities, + jlong effectiveCapabilities) { + pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids, + debug_flags, rlimits, + permittedCapabilities, effectiveCapabilities, + MOUNT_EXTERNAL_NONE, NULL, NULL, true, NULL); + if (pid > 0) { + // The zygote process checks whether the child process has died or not. + ALOGI("System server process %d has been created", pid); + gSystemServerPid = pid; + // There is a slight window that the system server process has crashed + // but it went unnoticed because we haven't published its pid yet. So + // we recheck here just to make sure that all is well. + int status; + if (waitpid(pid, &status, WNOHANG) == pid) { + ALOGE("System server process %d has died. Restarting Zygote!", pid); + RuntimeAbort(env); + } + } + return pid; +} + +static JNINativeMethod gMethods[] = { + { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I)I", + (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize }, + { "nativeForkSystemServer", "(II[II[[IJJ)I", + (void *) com_android_internal_os_Zygote_nativeForkSystemServer } +}; + +int register_com_android_internal_os_Zygote(JNIEnv* env) { + gZygoteClass = (jclass) env->NewGlobalRef(env->FindClass(kZygoteClassName)); + if (gZygoteClass == NULL) { + RuntimeAbort(env); + } + gCallPostForkChildHooks = env->GetStaticMethodID(gZygoteClass, "callPostForkChildHooks", "(I)V"); + + return AndroidRuntime::registerNativeMethods(env, "com/android/internal/os/Zygote", + gMethods, NELEM(gMethods)); +} +} // namespace android + diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 04f8009..84b56ce 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -16,7 +16,7 @@ package android.content.pm; -import static libcore.io.OsConstants.*; +import static android.system.OsConstants.*; import com.android.frameworks.coretests.R; import com.android.internal.content.PackageHelper; @@ -46,6 +46,9 @@ import android.os.storage.StorageManager; import android.os.storage.StorageResultCode; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStat; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.SmallTest; @@ -60,10 +63,6 @@ import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import libcore.io.ErrnoException; -import libcore.io.Libcore; -import libcore.io.StructStat; - public class PackageManagerTests extends AndroidTestCase { private static final boolean localLOGV = true; @@ -501,7 +500,7 @@ public class PackageManagerTests extends AndroidTestCase { final StructStat stat; try { - stat = Libcore.os.lstat(path); + stat = Os.lstat(path); } catch (ErrnoException e) { throw new AssertionError(reason + "\n" + "Got: " + path + " does not exist"); } diff --git a/core/tests/coretests/src/android/net/LinkAddressTest.java b/core/tests/coretests/src/android/net/LinkAddressTest.java index 17423be..bccf556 100644 --- a/core/tests/coretests/src/android/net/LinkAddressTest.java +++ b/core/tests/coretests/src/android/net/LinkAddressTest.java @@ -32,13 +32,13 @@ import android.os.Parcel; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import static libcore.io.OsConstants.IFA_F_DEPRECATED; -import static libcore.io.OsConstants.IFA_F_PERMANENT; -import static libcore.io.OsConstants.IFA_F_TENTATIVE; -import static libcore.io.OsConstants.RT_SCOPE_HOST; -import static libcore.io.OsConstants.RT_SCOPE_LINK; -import static libcore.io.OsConstants.RT_SCOPE_SITE; -import static libcore.io.OsConstants.RT_SCOPE_UNIVERSE; +import static android.system.OsConstants.IFA_F_DEPRECATED; +import static android.system.OsConstants.IFA_F_PERMANENT; +import static android.system.OsConstants.IFA_F_TENTATIVE; +import static android.system.OsConstants.RT_SCOPE_HOST; +import static android.system.OsConstants.RT_SCOPE_LINK; +import static android.system.OsConstants.RT_SCOPE_SITE; +import static android.system.OsConstants.RT_SCOPE_UNIVERSE; /** * Tests for {@link LinkAddress}. diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java index a602e07..553afe0 100644 --- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java +++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java @@ -18,14 +18,13 @@ package android.net; import android.net.LinkProperties; import android.net.RouteInfo; +import android.system.OsConstants; import android.test.suitebuilder.annotation.SmallTest; import junit.framework.TestCase; import java.net.InetAddress; import java.util.ArrayList; -import libcore.io.OsConstants; - public class LinkPropertiesTest extends TestCase { private static InetAddress ADDRV4 = NetworkUtils.numericToInetAddress("75.208.6.1"); private static InetAddress ADDRV6 = NetworkUtils.numericToInetAddress( diff --git a/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java index 0e3c13a..3f9e62e 100644 --- a/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java +++ b/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java @@ -46,7 +46,6 @@ import java.util.Random; import junit.framework.Assert; import libcore.io.IoUtils; -import libcore.io.Libcore; /** * Tests for {@link FileRotator}. diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index 1413319..ecd6a8b 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -404,6 +404,8 @@ key 484 B FUNCTION # key 503 KEY_BRL_DOT7 # key 504 KEY_BRL_DOT8 +key 580 APP_SWITCH + # Keys defined by HID usages key usage 0x0c006F BRIGHTNESS_UP key usage 0x0c0070 BRIGHTNESS_DOWN diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index 8b5609f..437d2f4 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -36,9 +36,21 @@ public final class Rect implements Parcelable { public int right; public int bottom; - private static final Pattern FLATTENED_PATTERN = Pattern.compile( + /** + * A helper class for flattened rectange pattern recognition. A separate + * class to avoid an initialization dependency on a regular expression + * causing Rect to not be initializable with an ahead-of-time compilation + * scheme. + */ + private static final class UnflattenHelper { + private static final Pattern FLATTENED_PATTERN = Pattern.compile( "(-?\\d+) (-?\\d+) (-?\\d+) (-?\\d+)"); + static Matcher getMatcher(String str) { + return FLATTENED_PATTERN.matcher(str); + } + } + /** * Create a new empty Rect. All coordinates are initialized to 0. */ @@ -152,7 +164,7 @@ public final class Rect implements Parcelable { * or null if the string is not of that form. */ public static Rect unflattenFromString(String str) { - Matcher matcher = FLATTENED_PATTERN.matcher(str); + Matcher matcher = UnflattenHelper.getMatcher(str); if (!matcher.matches()) { return null; } diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h index 649f4c3..3dfdb46 100644 --- a/include/android_runtime/AndroidRuntime.h +++ b/include/android_runtime/AndroidRuntime.h @@ -34,7 +34,7 @@ namespace android { class AndroidRuntime { public: - AndroidRuntime(); + AndroidRuntime(char* argBlockStart, size_t argBlockSize); virtual ~AndroidRuntime(); enum StartMode { @@ -44,6 +44,8 @@ public: Tool, }; + void setArgv0(const char* argv0); + /** * Register a set of methods in the specified class. */ @@ -53,8 +55,7 @@ public: /** * Call a class's static main method with the given arguments, */ - status_t callMain(const char* className, jclass clazz, int argc, - const char* const argv[]); + status_t callMain(const String8& className, jclass clazz, const Vector<String8>& args); /** * Find a class, with the input either of the form @@ -64,7 +65,7 @@ public: int addVmArguments(int argc, const char* const argv[]); - void start(const char *classname, const char* options); + void start(const char *classname, const Vector<String8>& options); void exit(int code); @@ -120,6 +121,8 @@ private: Vector<JavaVMOption> mOptions; bool mExitWithoutCleanup; + char* const mArgBlockStart; + const size_t mArgBlockLength; /* JNI JavaVM pointer */ static JavaVM* mJavaVM; diff --git a/include/private/hwui/DrawGlInfo.h b/include/private/hwui/DrawGlInfo.h index fc810be..236e64c 100644 --- a/include/private/hwui/DrawGlInfo.h +++ b/include/private/hwui/DrawGlInfo.h @@ -55,7 +55,8 @@ struct DrawGlInfo { kModeDraw, // Indicates the the functor is called only to perform // processing and that no draw should be attempted - kModeProcess + kModeProcess, + kModeProcessNoContext }; /** diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 0be17ff..8d19ca2 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -735,30 +735,34 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int // a null path is OK because there are no custom kernels used // hence nothing gets cached by RS if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { + mRs.clear(); ALOGE("blur RS failed to init"); + } else { + mRsElement = RSC::Element::A_8(mRs); + mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); } - - mRsElement = RSC::Element::A_8(mRs); - mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); } + if (mRs != 0) { + RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); + RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, + RS_ALLOCATION_MIPMAP_NONE, + RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, + *image); + RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, + RS_ALLOCATION_MIPMAP_NONE, + RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, + outImage); + + mRsScript->setRadius(radius); + mRsScript->setInput(ain); + mRsScript->forEach(aout); + + // replace the original image's pointer, avoiding a copy back to the original buffer + free(*image); + *image = outImage; - RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); - RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, - RS_ALLOCATION_MIPMAP_NONE, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, - *image); - RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, - RS_ALLOCATION_MIPMAP_NONE, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, - outImage); - - mRsScript->setRadius(radius); - mRsScript->setInput(ain); - mRsScript->forEach(aout); - - // replace the original image's pointer, avoiding a copy back to the original buffer - free(*image); - *image = outImage; - - return; + return; + } } #endif diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index cc6d0cd..5bdb18a 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -108,7 +108,7 @@ public: * Returns the current clip in local coordinates. The clip rect is * transformed by the inverse transform matrix. */ - const Rect& getLocalClip(); + ANDROID_API const Rect& getLocalClip(); /** * Resets the clip to the specified rect. diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index 8f5beb8..b493298 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -212,18 +212,28 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* int dstY = y + glyph->mBitmapTop; CacheTexture* cacheTexture = glyph->mCacheTexture; + PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer(); + uint32_t formatSize = PixelBuffer::formatSize(pixelBuffer->getFormat()); uint32_t cacheWidth = cacheTexture->getWidth(); - uint32_t startY = glyph->mStartY * cacheWidth; - uint32_t endY = startY + (glyph->mBitmapHeight * cacheWidth); + uint32_t srcStride = formatSize * cacheWidth; + uint32_t startY = glyph->mStartY * srcStride; + uint32_t endY = startY + (glyph->mBitmapHeight * srcStride); - PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer(); const uint8_t* cacheBuffer = pixelBuffer->map(); for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY; - cacheY += cacheWidth, bitmapY += bitmapWidth) { - memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth); + cacheY += srcStride, bitmapY += bitmapWidth) { + + if (formatSize == 1) { + memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth); + } else { + for (uint32_t i = 0; i < glyph->mBitmapWidth; ++i) { + bitmap[bitmapY + dstX + i] = cacheBuffer[cacheY + (glyph->mStartX + i)*formatSize]; + } + } } + } void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 53835e2..06a8f4c 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -44,6 +44,8 @@ import android.provider.Settings; import android.sax.Element; import android.sax.ElementListener; import android.sax.RootElement; +import android.system.ErrnoException; +import android.system.Os; import android.text.TextUtils; import android.util.Log; import android.util.Xml; @@ -59,9 +61,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Locale; -import libcore.io.ErrnoException; -import libcore.io.Libcore; - /** * Internal service helper that no-one should use directly. * @@ -1129,7 +1128,7 @@ public class MediaScanner if (path != null && path.startsWith("/")) { boolean exists = false; try { - exists = Libcore.os.access(path, libcore.io.OsConstants.F_OK); + exists = Os.access(path, android.system.OsConstants.F_OK); } catch (ErrnoException e1) { } if (!exists && !MtpConstants.isAbstractObject(format)) { @@ -1280,6 +1279,14 @@ public class MediaScanner mMediaProvider = null; } + private void releaseResources() { + // release the DrmManagerClient resources + if (mDrmManagerClient != null) { + mDrmManagerClient.release(); + mDrmManagerClient = null; + } + } + private void initialize(String volumeName) { mMediaProvider = mContext.getContentResolver().acquireProvider("media"); @@ -1340,6 +1347,8 @@ public class MediaScanner Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()", e); } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scan()", e); + } finally { + releaseResources(); } } @@ -1363,6 +1372,8 @@ public class MediaScanner } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); return null; + } finally { + releaseResources(); } } @@ -1477,6 +1488,7 @@ public class MediaScanner if (fileList != null) { fileList.close(); } + releaseResources(); } } diff --git a/media/java/android/media/videoeditor/VideoEditorImpl.java b/media/java/android/media/videoeditor/VideoEditorImpl.java index 2446c2f..fbf2eab 100644 --- a/media/java/android/media/videoeditor/VideoEditorImpl.java +++ b/media/java/android/media/videoeditor/VideoEditorImpl.java @@ -47,6 +47,8 @@ import android.os.Debug; import android.os.SystemProperties; import android.os.Environment; +import libcore.io.IoUtils; + /** * The VideoEditor implementation {@hide} */ @@ -1859,15 +1861,15 @@ public class VideoEditorImpl implements VideoEditor { } } + FileOutputStream stream = null; try { - FileOutputStream stream = new FileOutputStream(mProjectPath + "/" - + THUMBNAIL_FILENAME); + stream = new FileOutputStream(mProjectPath + "/" + THUMBNAIL_FILENAME); projectBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream); stream.flush(); - stream.close(); } catch (IOException e) { throw new IllegalArgumentException ("Error creating project thumbnail"); } finally { + IoUtils.closeQuietly(stream); projectBitmap.recycle(); } } diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 63a61e2..037c626 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -65,8 +65,6 @@ LOCAL_C_INCLUDES += \ LOCAL_CFLAGS += -LOCAL_LDLIBS := -lpthread - LOCAL_MODULE:= libmedia_jni include $(BUILD_SHARED_LIBRARY) diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 221ea57..5fd5dee 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -192,7 +192,7 @@ status_t JMediaCodec::dequeueOutputBuffer( env, env->FindClass("android/media/MediaCodec$BufferInfo")); jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V"); - env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags); + env->CallVoidMethod(bufferInfo, method, (jint)offset, (jint)size, timeUs, flags); return OK; } diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 6176f0f..c2694f1 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -262,6 +262,13 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env, width, height, config); + if (jBitmap == NULL) { + if (env->ExceptionCheck()) { + env->ExceptionClear(); + } + ALOGE("getFrameAtTime: create Bitmap failed!"); + return NULL; + } SkBitmap *bitmap = (SkBitmap *) env->GetLongField(jBitmap, fields.nativeBitmap); diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 0cfd2ff..b74d0fb 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -14,6 +14,13 @@ * limitations under the License. */ +#include <assert.h> +#include <fcntl.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <unistd.h> + //#define LOG_NDEBUG 0 #define LOG_TAG "MediaRecorderJNI" #include <utils/Log.h> @@ -22,11 +29,6 @@ #include <camera/ICameraService.h> #include <camera/Camera.h> #include <media/mediarecorder.h> -#include <stdio.h> -#include <assert.h> -#include <limits.h> -#include <unistd.h> -#include <fcntl.h> #include <utils/threads.h> #include "jni.h" @@ -303,7 +305,7 @@ android_media_MediaRecorder_setMaxFileSize( sp<MediaRecorder> mr = getMediaRecorder(env, thiz); char params[64]; - sprintf(params, "max-filesize=%lld", max_filesize_bytes); + sprintf(params, "max-filesize=%" PRId64, max_filesize_bytes); process_media_recorder_call(env, mr->setParameters(String8(params)), "java/lang/RuntimeException", "setMaxFileSize failed."); } diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp index 058012b..0894d74 100644 --- a/media/jni/mediaeditor/VideoEditorMain.cpp +++ b/media/jni/mediaeditor/VideoEditorMain.cpp @@ -16,6 +16,7 @@ #define LOG_NDEBUG 1 #define LOG_TAG "VideoEditorMain" #include <dlfcn.h> +#include <inttypes.h> #include <stdio.h> #include <unistd.h> #include <utils/Log.h> @@ -24,7 +25,6 @@ #include <VideoEditorJava.h> #include <VideoEditorOsal.h> #include <VideoEditorLogging.h> -#include <marker.h> #include <VideoEditorClasses.h> #include <VideoEditorThumbnailMain.h> #include <M4OSA_Debug.h> @@ -438,7 +438,7 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType, M4VS, (M4OSA_Char*)"videoEdito JNI overlayFile"); if (pContext->mOverlayFileName != NULL) { strncpy (pContext->mOverlayFileName, - (const char*)pContext->pEditSettings->\ + (const char*)pContext->pEditSettings-> Effects[overlayEffectIndex].xVSS.pFramingFilePath, overlayFileNameLen); //Change the name to png file extPos = strstr(pContext->mOverlayFileName, ".rgb"); @@ -1560,9 +1560,6 @@ videoEditor_populateSettings( int *pOverlayIndex = M4OSA_NULL; M4OSA_Char* pTempChar = M4OSA_NULL; - // Add a code marker (the condition must always be true). - ADD_CODE_MARKER_FUN(NULL != pEnv) - // Validate the settings parameter. videoEditJava_checkAndThrowIllegalArgumentException(&needToBeLoaded, pEnv, (NULL == settings), @@ -2196,10 +2193,6 @@ static jint videoEditor_getPixels( M4OSA_Context mContext = M4OSA_NULL; jint* m_dst32 = M4OSA_NULL; - - // Add a text marker (the condition must always be true). - ADD_TEXT_MARKER_FUN(NULL != env) - const char *pString = env->GetStringUTFChars(path, NULL); if (pString == M4OSA_NULL) { if (env != NULL) { @@ -2537,9 +2530,6 @@ videoEditor_init( VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEO_EDITOR", "videoEditor_init()"); - // Add a text marker (the condition must always be true). - ADD_TEXT_MARKER_FUN(NULL != pEnv) - // Get the context. pContext = (ManualEditContext*)videoEditClasses_getContext(&initialized, pEnv, thiz); @@ -2948,9 +2938,6 @@ videoEditor_loadSettings( VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEO_EDITOR", "videoEditor_loadSettings()"); - // Add a code marker (the condition must always be true). - ADD_CODE_MARKER_FUN(NULL != pEnv) - // Get the context. pContext = (ManualEditContext*)videoEditClasses_getContext(&needToBeLoaded, pEnv, thiz); @@ -3123,9 +3110,6 @@ videoEditor_release( VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEO_EDITOR", "videoEditor_release()"); - // Add a text marker (the condition must always be true). - ADD_TEXT_MARKER_FUN(NULL != pEnv) - // Get the context. pContext = (ManualEditContext*)videoEditClasses_getContext(&released, pEnv, thiz); @@ -3388,7 +3372,7 @@ M4OSA_ERR M4MA_generateAudioGraphFile(JNIEnv* pEnv, M4OSA_Char* pInputFileURL, err = M4OSA_fileReadOpen (&inputFileHandle, pInputFileURL, M4OSA_kFileRead); if (inputFileHandle == M4OSA_NULL) { VIDEOEDIT_LOG_ERROR(ANDROID_LOG_INFO, "VIDEO_EDITOR", - "M4MA_generateAudioGraphFile: Cannot open input file 0x%lx", err); + "M4MA_generateAudioGraphFile: Cannot open input file 0x%" PRIx32, err); return err; } @@ -3422,7 +3406,7 @@ M4OSA_ERR M4MA_generateAudioGraphFile(JNIEnv* pEnv, M4OSA_Char* pInputFileURL, bufferIn.m_bufferSize = samplesCountInBytes*sizeof(M4OSA_UInt16); } else { VIDEOEDIT_LOG_ERROR(ANDROID_LOG_INFO, "VIDEO_EDITOR", - "M4MA_generateAudioGraphFile: Malloc failed for bufferIn.m_dataAddress 0x%lx", + "M4MA_generateAudioGraphFile: Malloc failed for bufferIn.m_dataAddress 0x%" PRIx32, M4ERR_ALLOC); return M4ERR_ALLOC; } @@ -3462,7 +3446,7 @@ M4OSA_ERR M4MA_generateAudioGraphFile(JNIEnv* pEnv, M4OSA_Char* pInputFileURL, if (err != M4NO_ERROR) { // if out value of bytes-read is 0, break if ( numBytesToRead == 0) { - VIDEOEDIT_LOG_ERROR(ANDROID_LOG_INFO, "VIDEO_EDITOR", "numBytesToRead 0x%lx", + VIDEOEDIT_LOG_ERROR(ANDROID_LOG_INFO, "VIDEO_EDITOR", "numBytesToRead 0x%" PRIx32, numBytesToRead); break; /* stop if file is empty or EOF */ } @@ -3514,7 +3498,7 @@ M4OSA_ERR M4MA_generateAudioGraphFile(JNIEnv* pEnv, M4OSA_Char* pInputFileURL, } while (numBytesToRead != 0); - VIDEOEDIT_LOG_ERROR(ANDROID_LOG_INFO, "VIDEO_EDITOR", "loop 0x%lx", volumeValuesCount); + VIDEOEDIT_LOG_ERROR(ANDROID_LOG_INFO, "VIDEO_EDITOR", "loop 0x%" PRIx32, volumeValuesCount); /* if some error occured in fwrite */ if (numBytesToRead != 0) { @@ -3633,15 +3617,9 @@ jint JNI_OnLoad( VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", "JNI_OnLoad()"); - // Add a text marker (the condition must always be true). - ADD_TEXT_MARKER_FUN(NULL != pVm) - // Check the JNI version. if (pVm->GetEnv(&pEnv, JNI_VERSION_1_4) == JNI_OK) { - // Add a code marker (the condition must always be true). - ADD_CODE_MARKER_FUN(NULL != pEnv) - // Register the manual edit JNI methods. if (videoEditor_registerManualEditMethods((JNIEnv*)pEnv) == 0) { diff --git a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp index 2f8e357..ae1a80e 100644 --- a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp +++ b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp @@ -26,7 +26,6 @@ #include <VideoEditorOsal.h> #include <VideoEditorLogging.h> #include <VideoEditorOsal.h> -#include <marker.h> extern "C" { #include <M4OSA_Clock.h> @@ -107,9 +106,6 @@ jobject videoEditProp_getProperties( ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES", "videoEditProp_getProperties()"); - // Add a text marker (the condition must always be true). - ADD_TEXT_MARKER_FUN(NULL != pEnv) - // Initialize the classes. videoEditPropClass_init(&initialized, (JNIEnv*)pEnv); @@ -192,9 +188,6 @@ jobject videoEditProp_getProperties( // dereferencing of pClipProperties). if (gotten) { - // Add a code marker (the condition must always be true). - ADD_CODE_MARKER_FUN(NULL != pClipProperties) - // Log the API call. VIDEOEDIT_LOG_API( ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES", @@ -316,9 +309,6 @@ jobject videoEditProp_getProperties( videoEditOsal_free(pFile); pFile = M4OSA_NULL; - // Add a text marker (the condition must always be true). - ADD_TEXT_MARKER_FUN(NULL != pEnv) - // Return the Properties object. return(properties); } diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 5a2e261..4c024ae 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -882,7 +882,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback protected int[] mConfigSpec; private int[] filterConfigSpec(int[] configSpec) { - if (mEGLContextClientVersion != 2) { + if (mEGLContextClientVersion != 2 && mEGLContextClientVersion != 3) { return configSpec; } /* We know none of the subclasses define EGL_RENDERABLE_TYPE. @@ -892,7 +892,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback int[] newConfigSpec = new int[len + 2]; System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1); newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE; - newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ + if (mEGLContextClientVersion == 2) { + newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT; /* EGL_OPENGL_ES2_BIT */ + } else { + newConfigSpec[len] = EGLExt.EGL_OPENGL_ES3_BIT_KHR; /* EGL_OPENGL_ES3_BIT_KHR */ + } newConfigSpec[len+1] = EGL10.EGL_NONE; return newConfigSpec; } diff --git a/packages/DefaultContainerService/Android.mk b/packages/DefaultContainerService/Android.mk index 9961168..0de2c1f 100644 --- a/packages/DefaultContainerService/Android.mk +++ b/packages/DefaultContainerService/Android.mk @@ -7,7 +7,7 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := DefaultContainerService -LOCAL_REQUIRED_MODULES := libdefcontainer_jni +LOCAL_JNI_SHARED_LIBRARIES := libdefcontainer_jni LOCAL_CERTIFICATE := platform diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 6e34bbb..36c1d5c 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -30,6 +30,7 @@ import android.content.pm.PackageParser; import android.content.res.ObbInfo; import android.content.res.ObbScanner; import android.net.Uri; +import android.os.Build; import android.os.Environment; import android.os.Environment.UserEnvironment; import android.os.FileUtils; @@ -39,10 +40,11 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StatFs; -import android.os.SystemClock; import android.provider.Settings; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStatVfs; import android.util.DisplayMetrics; -import android.util.Log; import android.util.Slog; import com.android.internal.app.IMediaContainerService; @@ -67,11 +69,8 @@ import javax.crypto.CipherInputStream; import javax.crypto.Mac; import javax.crypto.NoSuchPaddingException; -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; import libcore.io.Streams; -import libcore.io.StructStatVfs; /* * This service copies a downloaded apk to a file passed in as @@ -246,7 +245,7 @@ public class DefaultContainerService extends IntentService { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); try { - final StructStatVfs stat = Libcore.os.statvfs(path); + final StructStatVfs stat = Os.statvfs(path); final long totalSize = stat.f_blocks * stat.f_bsize; final long availSize = stat.f_bavail * stat.f_bsize; return new long[] { totalSize, availSize }; @@ -343,11 +342,13 @@ public class DefaultContainerService extends IntentService { // The .apk file String codePath = packageURI.getPath(); File codeFile = new File(codePath); + NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codePath); + final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); // Calculate size of container needed to hold base APK. final int sizeMb; try { - sizeMb = calculateContainerSize(codeFile, isForwardLocked); + sizeMb = calculateContainerSize(handle, codeFile, abi, isForwardLocked); } catch (IOException e) { Slog.w(TAG, "Problem when trying to copy " + codeFile.getPath()); return null; @@ -378,7 +379,7 @@ public class DefaultContainerService extends IntentService { } try { - Libcore.os.chmod(resFile.getAbsolutePath(), 0640); + Os.chmod(resFile.getAbsolutePath(), 0640); } catch (ErrnoException e) { Slog.e(TAG, "Could not chown APK: " + e.getMessage()); PackageHelper.destroySdDir(newCid); @@ -400,7 +401,7 @@ public class DefaultContainerService extends IntentService { } try { - Libcore.os.chmod(publicZipFile.getAbsolutePath(), 0644); + Os.chmod(publicZipFile.getAbsolutePath(), 0644); } catch (ErrnoException e) { Slog.e(TAG, "Could not chown public resource file: " + e.getMessage()); PackageHelper.destroySdDir(newCid); @@ -410,7 +411,14 @@ public class DefaultContainerService extends IntentService { final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME); if (sharedLibraryDir.mkdir()) { - int ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(codeFile, sharedLibraryDir); + int ret = PackageManager.INSTALL_SUCCEEDED; + if (abi >= 0) { + ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, + sharedLibraryDir, Build.SUPPORTED_ABIS[abi]); + } else if (abi != PackageManager.NO_NATIVE_LIBRARIES) { + ret = abi; + } + if (ret != PackageManager.INSTALL_SUCCEEDED) { Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath()); PackageHelper.destroySdDir(newCid); @@ -824,6 +832,17 @@ public class DefaultContainerService extends IntentService { return availSdMb > sizeMb; } + private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException { + NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(apkFile); + final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); + + try { + return calculateContainerSize(handle, apkFile, abi, forwardLocked); + } finally { + handle.close(); + } + } + /** * Calculate the container size for an APK. Takes into account the * @@ -831,7 +850,8 @@ public class DefaultContainerService extends IntentService { * @return size in megabytes (2^20 bytes) * @throws IOException when there is a problem reading the file */ - private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException { + private int calculateContainerSize(NativeLibraryHelper.ApkHandle apkHandle, + File apkFile, int abiIndex, boolean forwardLocked) throws IOException { // Calculate size of container needed to hold base APK. long sizeBytes = apkFile.length(); if (sizeBytes == 0 && !apkFile.exists()) { @@ -840,7 +860,10 @@ public class DefaultContainerService extends IntentService { // Check all the native files that need to be copied and add that to the // container size. - sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(apkFile); + if (abiIndex >= 0) { + sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(apkHandle, + Build.SUPPORTED_ABIS[abiIndex]); + } if (forwardLocked) { sizeBytes += PackageHelper.extractPublicFiles(apkFile.getPath(), null); diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index b2b2bd8..9069a55 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -436,6 +436,8 @@ public class DirectoryFragment extends Fragment { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { mode.getMenuInflater().inflate(R.menu.mode_directory, menu); + mode.setTitle(getResources() + .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount())); return true; } diff --git a/packages/InputDevices/res/raw/keyboard_layout_czech.kcm b/packages/InputDevices/res/raw/keyboard_layout_czech.kcm index f710a8e..dc614db 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_czech.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_czech.kcm @@ -13,7 +13,7 @@ # limitations under the License. # -# Czech keyboard layout. +# Czech (EU - qwerty) keyboard layout. # type OVERLAY @@ -26,6 +26,8 @@ key GRAVE { label: ';' base: ';' shift: '\u00b0' + ralt: '\u0060' + shift+ralt: '\u007e' } key 1 { @@ -38,6 +40,7 @@ key 1 { key 2 { label: '2' base: '\u011b' + capslock: '\u011a' shift: '2' ralt: '@' } @@ -45,6 +48,7 @@ key 2 { key 3 { label: '3' base: '\u0161' + capslock: '\u0160' shift: '3' ralt: '#' } @@ -52,6 +56,7 @@ key 3 { key 4 { label: '4' base: '\u010d' + capslock: '\u010c' shift: '4' ralt: '$' } @@ -59,6 +64,7 @@ key 4 { key 5 { label: '5' base: '\u0159' + capslock: '\u0158' shift: '5' ralt: '%' } @@ -66,6 +72,7 @@ key 5 { key 6 { label: '6' base: '\u017e' + capslock: '\u017d' shift: '6' ralt: '^' } @@ -73,6 +80,7 @@ key 6 { key 7 { label: '7' base: '\u00fd' + capslock: '\u00dd' shift: '7' ralt: '&' } @@ -80,6 +88,7 @@ key 7 { key 8 { label: '8' base: '\u00e1' + capslock: '\u00c1' shift: '8' ralt: '*' } @@ -87,6 +96,7 @@ key 8 { key 9 { label: '9' base: '\u00ed' + capslock: '\u00cd' shift: '9' ralt: '(' } @@ -94,6 +104,7 @@ key 9 { key 0 { label: '0' base: '\u00e9' + capslock: '\u00c9' shift: '0' ralt: ')' } @@ -180,6 +191,7 @@ key P { key LEFT_BRACKET { label: '\u00fa' base: '\u00fa' + capslock: '\u00da' shift: '/' ralt: '[' ralt+shift: '{' @@ -252,6 +264,7 @@ key L { key SEMICOLON { label: '\u016f' base: '\u016f' + capslock: '\u016e' shift: '"' ralt: ';' ralt+shift: ':' @@ -261,8 +274,8 @@ key APOSTROPHE { label: '\u00a7' base: '\u00a7' shift: '!' - ralt: '\'' - ralt+shift: '"' + ralt: '\u00a4' + ralt+shift: '\u005e' } key BACKSLASH { @@ -279,6 +292,8 @@ key PLUS { label: '\\' base: '\\' shift: '|' + ralt: '\u00df' + shift+ralt: '\u02dd' } key Z { @@ -330,6 +345,7 @@ key COMMA { base: ',' shift: '?' ralt: '<' + shift+ralt: '\u00d7' } key PERIOD { @@ -337,6 +353,7 @@ key PERIOD { base: '.' shift: ':' ralt: '>' + shift+ralt: '\u00f7' } key SLASH { diff --git a/packages/InputDevices/res/raw/keyboard_layout_english_us_intl.kcm b/packages/InputDevices/res/raw/keyboard_layout_english_us_intl.kcm index 0fabf02..66c1c98 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_english_us_intl.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_english_us_intl.kcm @@ -122,7 +122,7 @@ key Q { base: 'q' shift, capslock: 'Q' ralt: '\u00e4' - shift+ralt: '\u00c4' + shift+ralt, capslock+ralt: '\u00c4' } key W { @@ -130,7 +130,7 @@ key W { base: 'w' shift, capslock: 'W' ralt: '\u00e5' - shift+ralt: '\u00c5' + shift+ralt, capslock+ralt: '\u00c5' } key E { @@ -138,7 +138,7 @@ key E { base: 'e' shift, capslock: 'E' ralt: '\u00e9' - shift+ralt: '\u00c9' + shift+ralt, capslock+ralt: '\u00c9' } key R { @@ -153,7 +153,7 @@ key T { base: 't' shift, capslock: 'T' ralt: '\u00fe' - shift+ralt: '\u00de' + shift+ralt, capslock+ralt: '\u00de' } key Y { @@ -161,7 +161,7 @@ key Y { base: 'y' shift, capslock: 'Y' ralt: '\u00fc' - shift+ralt: '\u00dc' + shift+ralt, capslock+ralt: '\u00dc' } key U { @@ -169,7 +169,7 @@ key U { base: 'u' shift, capslock: 'U' ralt: '\u00fa' - shift+ralt: '\u00da' + shift+ralt, capslock+ralt: '\u00da' } key I { @@ -177,7 +177,7 @@ key I { base: 'i' shift, capslock: 'I' ralt: '\u00ed' - shift+ralt: '\u00cd' + shift+ralt, capslock+ralt: '\u00cd' } key O { @@ -185,7 +185,7 @@ key O { base: 'o' shift, capslock: 'O' ralt: '\u00f3' - shift+ralt: '\u00d3' + shift+ralt, capslock+ralt: '\u00d3' } key P { @@ -193,7 +193,7 @@ key P { base: 'p' shift, capslock: 'P' ralt: '\u00f6' - shift+ralt: '\u00d6' + shift+ralt, capslock+ralt: '\u00d6' } key LEFT_BRACKET { @@ -225,7 +225,7 @@ key A { base: 'a' shift, capslock: 'A' ralt: '\u00e1' - shift+ralt: '\u00c1' + shift+ralt, ralt+capslock: '\u00c1' } key S { @@ -241,7 +241,7 @@ key D { base: 'd' shift, capslock: 'D' ralt: '\u00f0' - shift+ralt: '\u00d0' + shift+ralt, capslock+ralt: '\u00d0' } key F { @@ -279,7 +279,7 @@ key L { base: 'l' shift, capslock: 'L' ralt: '\u00f8' - shift+ralt: '\u00d8' + shift+ralt, capslock+ralt: '\u00d8' } key SEMICOLON { @@ -313,7 +313,7 @@ key Z { base: 'z' shift, capslock: 'Z' ralt: '\u00e6' - shift+ralt: '\u00c6' + shift+ralt, capslock+ralt: '\u00c6' } key X { @@ -347,7 +347,7 @@ key N { base: 'n' shift, capslock: 'N' ralt: '\u00f1' - shift+ralt: '\u00d1' + shift+ralt, capslock+ralt: '\u00d1' } key M { @@ -362,7 +362,7 @@ key COMMA { base: ',' shift: '<' ralt: '\u00e7' - shift+ralt: '\u00c7' + shift+ralt, capslock+ralt: '\u00c7' } key PERIOD { diff --git a/packages/InputDevices/res/raw/keyboard_layout_slovak.kcm b/packages/InputDevices/res/raw/keyboard_layout_slovak.kcm index 70c1fa4..2eb0f63 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_slovak.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_slovak.kcm @@ -13,7 +13,7 @@ # limitations under the License. # -# Slovak keyboard layout. +# Slovak (EU - qwerty) keyboard layout. # type OVERLAY @@ -26,94 +26,90 @@ key GRAVE { label: ';' base: ';' shift: '\u00b0' - ralt: '`' - ralt+shift: '~' } key 1 { label: '1' base: '+' shift: '1' - ralt: '!' + ralt: '~' } key 2 { label: '2' base: '\u013e' shift: '2' - ralt: '@' + ralt: '\u02c7' } key 3 { label: '3' base: '\u0161' shift: '3' - ralt: '#' + ralt: '\u0302' } key 4 { label: '4' base: '\u010d' shift: '4' - ralt: '$' + ralt: '\u02d8' } key 5 { label: '5' base: '\u0165' shift: '5' - ralt: '%' + ralt: '\u00b0' } key 6 { label: '6' base: '\u017e' shift: '6' - ralt: '^' + ralt: '\u02db' } key 7 { label: '7' base: '\u00fd' shift: '7' - ralt: '&' + ralt: '\u0300' } key 8 { label: '8' base: '\u00e1' shift: '8' - ralt: '*' + ralt: '\u02d9' } key 9 { label: '9' base: '\u00ed' shift: '9' - ralt: '(' + ralt: '\u0301' } key 0 { label: '0' base: '\u00e9' shift: '0' - ralt: ')' + ralt: '\u02dd' } key MINUS { label: '=' base: '=' shift: '%' - ralt: '-' - ralt+shift: '_' + ralt: '\u0308' } key EQUALS { label: '\u00b4' base: '\u0301' shift: '\u030c' - ralt: '=' - ralt+shift: '+' + ralt: '\u00b8' } ### ROW 2 @@ -179,22 +175,21 @@ key P { label: 'P' base: 'p' shift, capslock: 'P' + ralt: '\'' } key LEFT_BRACKET { label: '\u00fa' base: '\u00fa' shift: '/' - ralt: '[' - ralt+shift: '{' + ralt: '\u00f7' } key RIGHT_BRACKET { label: '\u00e4' base: '\u00e4' shift: '(' - ralt: ']' - ralt+shift: '}' + ralt: '\u00d7' } ### ROW 3 @@ -209,24 +204,28 @@ key S { label: 'S' base: 's' shift, capslock: 'S' + ralt: '\u0111' } key D { label: 'D' base: 'd' shift, capslock: 'D' + ralt: '\u0110' } key F { label: 'F' base: 'f' shift, capslock: 'F' + ralt: '[' } key G { label: 'G' base: 'g' shift, capslock: 'G' + ralt: ']' } key H { @@ -245,64 +244,65 @@ key K { label: 'K' base: 'k' shift, capslock: 'K' + ralt: '\u0142' } key L { label: 'L' base: 'l' shift, capslock: 'L' + ralt: '\u0141' } key SEMICOLON { label: '\u00f4' base: '\u00f4' shift: '"' - ralt: ';' - ralt+shift: ':' + ralt: '$' } key APOSTROPHE { label: '\u00a7' base: '\u00a7' shift: '!' - ralt: '\'' - ralt+shift: '"' + ralt: '\u00df' } key BACKSLASH { label: '\u0148' base: '\u0148' shift: ')' - ralt: '\\' - ralt+shift: '|' + ralt: '\u00a4' } ### ROW 4 key PLUS { - label: '\\' - base: '\\' - shift: '|' - ralt: '&' - ralt+shift: '*' + label: '&' + base: '&' + shift: '*' + ralt: '<' } key Z { label: 'Z' base: 'z' shift, capslock: 'Z' + ralt: '>' } key X { label: 'X' base: 'x' shift, capslock: 'X' + ralt: '#' } key C { label: 'C' base: 'c' shift, capslock: 'C' + ralt: '&' } key V { @@ -316,12 +316,14 @@ key B { label: 'B' base: 'b' shift, capslock: 'B' + ralt: '{' } key N { label: 'N' base: 'n' shift, capslock: 'N' + ralt: '}' } key M { @@ -348,6 +350,5 @@ key SLASH { label: '-' base: '-' shift: '_' - ralt: '/' - ralt+shift: '?' + ralt: '*' } diff --git a/packages/InputDevices/res/raw/keyboard_layout_swiss_french.kcm b/packages/InputDevices/res/raw/keyboard_layout_swiss_french.kcm index a75d154..9e20462 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_swiss_french.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_swiss_french.kcm @@ -56,12 +56,14 @@ key 4 { label: '4' base: '4' shift: '\u00e7' + ralt: '\u00b0' } key 5 { label: '5' base: '5' shift: '%' + ralt: '\u00a7' } key 6 { diff --git a/packages/InputDevices/res/raw/keyboard_layout_swiss_german.kcm b/packages/InputDevices/res/raw/keyboard_layout_swiss_german.kcm index ae93f4b..7fbd1a9 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_swiss_german.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_swiss_german.kcm @@ -56,12 +56,14 @@ key 4 { label: '4' base: '4' shift: '\u00e7' + ralt: '\u00b0' } key 5 { label: '5' base: '5' shift: '%' + ralt: '\u00a7' } key 6 { @@ -178,6 +180,8 @@ key LEFT_BRACKET { label: '\u00fc' base: '\u00fc' shift: '\u00e8' + capslock: '\u00dc' + capslock+shift: '\u00c8' ralt: '[' } @@ -248,12 +252,16 @@ key SEMICOLON { label: '\u00f6' base: '\u00f6' shift: '\u00e9' + capslock: '\u00d6' + capslock+shift: '\u00c9' } key APOSTROPHE { label: '\u00e4' base: '\u00e4' shift: '\u00e0' + capslock: '\u00c4' + capslock+shift: '\u00c0' ralt: '{' } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java index 751572c..bcb2e6d 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java @@ -32,12 +32,11 @@ import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.MutableInt; import android.util.Slog; import android.view.View; import android.widget.TextView; -import libcore.util.MutableInt; - import java.lang.ref.WeakReference; import com.android.internal.widget.LockPatternUtils; diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 4b0c2cb..d940406 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -202,6 +202,7 @@ public class ImageWallpaper extends WallpaperService { if (mReceiver != null) { unregisterReceiver(mReceiver); } + mBackground = null; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index ed00398..604f4c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -841,6 +841,9 @@ public abstract class BaseStatusBar extends SystemUI implements } protected void addNotificationViews(NotificationData.Entry entry) { + if (entry == null) { + return; + } // Add the expanded view and icon. int pos = mNotificationData.add(entry); if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 8957a77..194774d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -168,6 +168,9 @@ public class PhoneStatusBarPolicy { if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { mSimState = IccCardConstants.State.ABSENT; } + else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) { + mSimState = IccCardConstants.State.CARD_IO_ERROR; + } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { mSimState = IccCardConstants.State.READY; } diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk index d9566d5..3c4e951 100644 --- a/packages/services/PacProcessor/Android.mk +++ b/packages/services/PacProcessor/Android.mk @@ -25,7 +25,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := PacProcessor LOCAL_CERTIFICATE := platform -LOCAL_REQUIRED_MODULES := libjni_pacprocessor +LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor include $(BUILD_PACKAGE) diff --git a/preloaded-classes b/preloaded-classes index 02bd0bc..b60a379 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -400,7 +400,6 @@ android.ddm.DdmHandleProfiling android.ddm.DdmHandleThread android.ddm.DdmHandleViewDebug android.ddm.DdmRegister -android.debug.JNITest android.drm.DrmManagerClient android.emoji.EmojiFactory android.graphics.AvoidXfermode diff --git a/rs/java/android/renderscript/RenderScriptGL.java b/rs/java/android/renderscript/RenderScriptGL.java index c9cbe3e..d6841c8 100644 --- a/rs/java/android/renderscript/RenderScriptGL.java +++ b/rs/java/android/renderscript/RenderScriptGL.java @@ -232,9 +232,13 @@ public class RenderScriptGL extends RenderScript { validate(); //android.util.Log.v("rs", "set surface " + sur + " w=" + w + ", h=" + h); + Surface s = null; + if (sur != null) { + s = new Surface(sur); + } mWidth = w; mHeight = h; - nContextSetSurfaceTexture(w, h, sur); + nContextSetSurface(w, h, s); } /** diff --git a/rs/java/android/renderscript/ScriptIntrinsicResize.java b/rs/java/android/renderscript/ScriptIntrinsicResize.java new file mode 100644 index 0000000..cc37120 --- /dev/null +++ b/rs/java/android/renderscript/ScriptIntrinsicResize.java @@ -0,0 +1,113 @@ +/* + * 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 android.renderscript; + +/** + * Intrinsic for performing a resize of a 2D allocation. + * @hide + */ +public final class ScriptIntrinsicResize extends ScriptIntrinsic { + private Allocation mInput; + + private ScriptIntrinsicResize(long id, RenderScript rs) { + super(id, rs); + } + + /** + * Supported elements types are {@link Element#U8}, {@link + * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4} + * + * @param rs The RenderScript context + * + * @return ScriptIntrinsicResize + */ + public static ScriptIntrinsicResize create(RenderScript rs) { + long id = rs.nScriptIntrinsicCreate(12, 0); + ScriptIntrinsicResize si = new ScriptIntrinsicResize(id, rs); + return si; + + } + + /** + * Set the input of the resize. + * Must match the element type supplied during create. + * + * @param ain The input allocation. + */ + public void setInput(Allocation ain) { + Element e = ain.getElement(); + if (!e.isCompatible(Element.U8(mRS)) && + !e.isCompatible(Element.U8_2(mRS)) && + !e.isCompatible(Element.U8_3(mRS)) && + !e.isCompatible(Element.U8_4(mRS))) { + throw new RSIllegalArgumentException("Unsuported element type."); + } + + mInput = ain; + setVar(0, ain); + } + + /** + * Get a FieldID for the input field of this intrinsic. + * + * @return Script.FieldID The FieldID object. + */ + public Script.FieldID getFieldID_Input() { + return createFieldID(0, null); + } + + + /** + * Resize copy the input allocation to the output specified. The + * Allocation is rescaled if necessary using bi-cubic + * interpolation. + * + * @param aout Output allocation. Element type must match + * current input. Must not be same as input. + */ + public void forEach_bicubic(Allocation aout) { + if (aout == mInput) { + throw new RSIllegalArgumentException("Output cannot be same as Input."); + } + forEach_bicubic(aout, null); + } + + /** + * Resize copy the input allocation to the output specified. The + * Allocation is rescaled if necessary using bi-cubic + * interpolation. + * + * @param aout Output allocation. Element type must match + * current input. + * @param opt LaunchOptions for clipping + */ + public void forEach_bicubic(Allocation aout, Script.LaunchOptions opt) { + forEach(0, null, aout, null, opt); + } + + /** + * Get a KernelID for this intrinsic kernel. + * + * @return Script.KernelID The KernelID object. + */ + public Script.KernelID getKernelID_bicubic() { + return createKernelID(0, 2, null, null); + } + + +} + diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk index cbb5b3b..e69432d 100644 --- a/rs/jni/Android.mk +++ b/rs/jni/Android.mk @@ -28,7 +28,6 @@ LOCAL_C_INCLUDES += \ LOCAL_CFLAGS += -Wno-unused-parameter -LOCAL_LDLIBS := -lpthread LOCAL_ADDITIONAL_DEPENDENCIES := $(addprefix $(rs_generated_include_dir)/,rsgApiFuncDecl.h) LOCAL_MODULE:= librs_jni LOCAL_ADDITIONAL_DEPENDENCIES += $(rs_generated_source) diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 9b89eb9..0d75f4c 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -50,24 +50,29 @@ using namespace android; -#define PER_ARRAY_TYPE(flag, fnc, ...) { \ +#define PER_ARRAY_TYPE(flag, fnc, readonly, ...) { \ jint len = 0; \ void *ptr = NULL; \ size_t typeBytes = 0; \ + jint relFlag = 0; \ + if (readonly) { \ + /* The on-release mode should only be JNI_ABORT for read-only accesses. */ \ + relFlag = JNI_ABORT; \ + } \ switch(dataType) { \ case RS_TYPE_FLOAT_32: \ len = _env->GetArrayLength((jfloatArray)data); \ ptr = _env->GetFloatArrayElements((jfloatArray)data, flag); \ typeBytes = 4; \ fnc(__VA_ARGS__); \ - _env->ReleaseFloatArrayElements((jfloatArray)data, (jfloat *)ptr, JNI_ABORT); \ + _env->ReleaseFloatArrayElements((jfloatArray)data, (jfloat *)ptr, relFlag); \ return; \ case RS_TYPE_FLOAT_64: \ len = _env->GetArrayLength((jdoubleArray)data); \ ptr = _env->GetDoubleArrayElements((jdoubleArray)data, flag); \ typeBytes = 8; \ fnc(__VA_ARGS__); \ - _env->ReleaseDoubleArrayElements((jdoubleArray)data, (jdouble *)ptr, JNI_ABORT);\ + _env->ReleaseDoubleArrayElements((jdoubleArray)data, (jdouble *)ptr, relFlag); \ return; \ case RS_TYPE_SIGNED_8: \ case RS_TYPE_UNSIGNED_8: \ @@ -75,7 +80,7 @@ using namespace android; ptr = _env->GetByteArrayElements((jbyteArray)data, flag); \ typeBytes = 1; \ fnc(__VA_ARGS__); \ - _env->ReleaseByteArrayElements((jbyteArray)data, (jbyte*)ptr, JNI_ABORT); \ + _env->ReleaseByteArrayElements((jbyteArray)data, (jbyte*)ptr, relFlag); \ return; \ case RS_TYPE_SIGNED_16: \ case RS_TYPE_UNSIGNED_16: \ @@ -83,7 +88,7 @@ using namespace android; ptr = _env->GetShortArrayElements((jshortArray)data, flag); \ typeBytes = 2; \ fnc(__VA_ARGS__); \ - _env->ReleaseShortArrayElements((jshortArray)data, (jshort *)ptr, JNI_ABORT); \ + _env->ReleaseShortArrayElements((jshortArray)data, (jshort *)ptr, relFlag); \ return; \ case RS_TYPE_SIGNED_32: \ case RS_TYPE_UNSIGNED_32: \ @@ -91,7 +96,7 @@ using namespace android; ptr = _env->GetIntArrayElements((jintArray)data, flag); \ typeBytes = 4; \ fnc(__VA_ARGS__); \ - _env->ReleaseIntArrayElements((jintArray)data, (jint *)ptr, JNI_ABORT); \ + _env->ReleaseIntArrayElements((jintArray)data, (jint *)ptr, relFlag); \ return; \ case RS_TYPE_SIGNED_64: \ case RS_TYPE_UNSIGNED_64: \ @@ -99,7 +104,7 @@ using namespace android; ptr = _env->GetLongArrayElements((jlongArray)data, flag); \ typeBytes = 8; \ fnc(__VA_ARGS__); \ - _env->ReleaseLongArrayElements((jlongArray)data, (jlong *)ptr, JNI_ABORT); \ + _env->ReleaseLongArrayElements((jlongArray)data, (jlong *)ptr, relFlag); \ return; \ default: \ break; \ @@ -675,6 +680,7 @@ static void ReleaseBitmapCallback(void *bmp) } +// Copies from the Java object data into the Allocation pointed to by _alloc. static void nAllocationData1D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint offset, jint lod, jint count, jobject data, jint sizeBytes, jint dataType) @@ -682,9 +688,10 @@ nAllocationData1D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint off RsAllocation *alloc = (RsAllocation *)_alloc; LOG_API("nAllocation1DData, con(%p), adapter(%p), offset(%i), count(%i), sizeBytes(%i), dataType(%i)", (RsContext)con, (RsAllocation)alloc, offset, count, sizeBytes, dataType); - PER_ARRAY_TYPE(NULL, rsAllocation1DData, (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes); + PER_ARRAY_TYPE(NULL, rsAllocation1DData, true, (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes); } +// Copies from the Java array data into the Allocation pointed to by alloc. static void // native void rsnAllocationElementData1D(long con, long id, int xoff, int compIdx, byte[] d, int sizeBytes); nAllocationElementData1D(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jint offset, jint lod, jint compIdx, jbyteArray data, jint sizeBytes) @@ -696,6 +703,7 @@ nAllocationElementData1D(JNIEnv *_env, jobject _this, jlong con, jlong alloc, ji _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT); } +// Copies from the Java object data into the Allocation pointed to by _alloc. static void nAllocationData2D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint lod, jint _face, jint w, jint h, jobject data, jint sizeBytes, jint dataType) @@ -704,9 +712,11 @@ nAllocationData2D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xof RsAllocationCubemapFace face = (RsAllocationCubemapFace)_face; LOG_API("nAllocation2DData, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i) type(%i)", (RsContext)con, alloc, xoff, yoff, w, h, sizeBytes, dataType); - PER_ARRAY_TYPE(NULL, rsAllocation2DData, (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0); + PER_ARRAY_TYPE(NULL, rsAllocation2DData, true, (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0); } +// Copies from the Allocation pointed to by srcAlloc into the Allocation +// pointed to by dstAlloc. static void nAllocationData2D_alloc(JNIEnv *_env, jobject _this, jlong con, jlong dstAlloc, jint dstXoff, jint dstYoff, @@ -731,6 +741,7 @@ nAllocationData2D_alloc(JNIEnv *_env, jobject _this, jlong con, srcMip, srcFace); } +// Copies from the Java object data into the Allocation pointed to by _alloc. static void nAllocationData3D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint zoff, jint lod, jint w, jint h, jint d, jobject data, int sizeBytes, int dataType) @@ -738,9 +749,11 @@ nAllocationData3D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xof RsAllocation *alloc = (RsAllocation *)_alloc; LOG_API("nAllocation3DData, con(%p), alloc(%p), xoff(%i), yoff(%i), zoff(%i), lod(%i), w(%i), h(%i), d(%i), sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, sizeBytes); - PER_ARRAY_TYPE(NULL, rsAllocation3DData, (RsContext)con, alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0); + PER_ARRAY_TYPE(NULL, rsAllocation3DData, true, (RsContext)con, alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0); } +// Copies from the Allocation pointed to by srcAlloc into the Allocation +// pointed to by dstAlloc. static void nAllocationData3D_alloc(JNIEnv *_env, jobject _this, jlong con, jlong dstAlloc, jint dstXoff, jint dstYoff, jint dstZoff, @@ -764,14 +777,16 @@ nAllocationData3D_alloc(JNIEnv *_env, jobject _this, jlong con, } +// Copies from the Allocation pointed to by _alloc into the Java object data. static void nAllocationRead(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jobject data, int dataType) { RsAllocation *alloc = (RsAllocation *)_alloc; LOG_API("nAllocationRead, con(%p), alloc(%p)", (RsContext)con, (RsAllocation)alloc); - PER_ARRAY_TYPE(0, rsAllocationRead, (RsContext)con, alloc, ptr, len * typeBytes); + PER_ARRAY_TYPE(0, rsAllocationRead, false, (RsContext)con, alloc, ptr, len * typeBytes); } +// Copies from the Allocation pointed to by _alloc into the Java object data. static void nAllocationRead1D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint offset, jint lod, jint count, jobject data, int sizeBytes, int dataType) @@ -779,9 +794,10 @@ nAllocationRead1D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint off RsAllocation *alloc = (RsAllocation *)_alloc; LOG_API("nAllocation1DRead, con(%p), adapter(%p), offset(%i), count(%i), sizeBytes(%i), dataType(%i)", (RsContext)con, alloc, offset, count, sizeBytes, dataType); - PER_ARRAY_TYPE(0, rsAllocation1DRead, (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes); + PER_ARRAY_TYPE(0, rsAllocation1DRead, false, (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes); } +// Copies from the Allocation pointed to by _alloc into the Java object data. static void nAllocationRead2D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint lod, jint _face, jint w, jint h, jobject data, int sizeBytes, int dataType) @@ -790,7 +806,7 @@ nAllocationRead2D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xof RsAllocationCubemapFace face = (RsAllocationCubemapFace)_face; LOG_API("nAllocation2DRead, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i) type(%i)", (RsContext)con, alloc, xoff, yoff, w, h, sizeBytes, dataType); - PER_ARRAY_TYPE(0, rsAllocation2DRead, (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0); + PER_ARRAY_TYPE(0, rsAllocation2DRead, false, (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0); } static jlong @@ -1026,7 +1042,7 @@ nScriptGetVarV(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jint len = _env->GetArrayLength(data); jbyte *ptr = _env->GetByteArrayElements(data, NULL); rsScriptGetVarV((RsContext)con, (RsScript)script, slot, ptr, len); - _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT); + _env->ReleaseByteArrayElements(data, ptr, 0); } static void diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 06a57d5..0da8489 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -1968,7 +1968,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, xOffset = dispatchEntry->xOffset * scaleFactor; yOffset = dispatchEntry->yOffset * scaleFactor; if (scaleFactor != 1.0f) { - for (size_t i = 0; i < motionEntry->pointerCount; i++) { + for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { scaledCoords[i] = motionEntry->pointerCoords[i]; scaledCoords[i].scale(scaleFactor); } @@ -1981,7 +1981,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // We don't want the dispatch target to know. if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { - for (size_t i = 0; i < motionEntry->pointerCount; i++) { + for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { scaledCoords[i].clear(); } usingCoords = scaledCoords; diff --git a/services/input/InputWindow.cpp b/services/input/InputWindow.cpp index fe61918..18cd8eb 100644 --- a/services/input/InputWindow.cpp +++ b/services/input/InputWindow.cpp @@ -36,6 +36,7 @@ bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { bool InputWindowInfo::isTrustedOverlay() const { return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG + || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY; } diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h index 28fa7ab..bd325b5 100644 --- a/services/input/InputWindow.h +++ b/services/input/InputWindow.h @@ -105,6 +105,7 @@ struct InputWindowInfo { TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19, TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20, TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21, + TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27, LAST_SYSTEM_WINDOW = 2999, }; diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index f73a92b..1fa0735 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -187,7 +187,7 @@ class MountService extends IMountService.Stub public static final int FstrimCompleted = 700; } - private Context mContext; + private final Context mContext; private NativeDaemonConnector mConnector; private final Object mVolumesLock = new Object(); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index f226972..8a3bc88 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -46,6 +46,7 @@ import android.view.WindowManager; import com.android.internal.R; import com.android.internal.os.BinderInternal; +import com.android.internal.os.Zygote; import com.android.internal.os.SamplingProfilerIntegration; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accounts.AccountManagerService; @@ -59,6 +60,7 @@ import com.android.server.media.MediaRouterService; import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; import com.android.server.os.SchedulingPolicyService; +import com.android.server.pm.BackgroundDexOptService; import com.android.server.pm.Installer; import com.android.server.pm.PackageManagerService; import com.android.server.pm.UserManagerService; @@ -71,7 +73,6 @@ import com.android.server.wifi.WifiService; import com.android.server.wm.WindowManagerService; import dalvik.system.VMRuntime; -import dalvik.system.Zygote; import java.io.File; import java.util.Timer; @@ -815,6 +816,13 @@ class ServerThread { } catch (Throwable e) { reportWtf("starting MediaRouterService", e); } + + try { + Slog.i(TAG, "BackgroundDexOptService"); + new BackgroundDexOptService(context); + } catch (Throwable e) { + reportWtf("starting BackgroundDexOptService", e); + } } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 4258a1b..3df1698 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -38,6 +38,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.TransferPipe; +import com.android.internal.os.Zygote; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.MemInfoReader; @@ -57,8 +58,6 @@ import com.android.server.wm.WindowManagerService; import com.google.android.collect.Lists; import com.google.android.collect.Maps; -import dalvik.system.Zygote; - import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; @@ -925,7 +924,7 @@ public final class ActivityManagerService extends ActivityManagerNative /** * This is set if we had to do a delayed dexopt of an app before launching - * it, to increasing the ANR timeouts in that case. + * it, to increase the ANR timeouts in that case. */ boolean mDidDexOpt; @@ -2781,11 +2780,16 @@ public final class ActivityManagerService extends ActivityManagerNative debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; } + String requiredAbi = app.info.cpuAbi; + if (requiredAbi == null) { + requiredAbi = Build.SUPPORTED_ABIS[0]; + } + // Start the process. It will either succeed and return a result containing // the PID of the new process, or else throw a RuntimeException. Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", app.processName, uid, uid, gids, debugFlags, mountExternal, - app.info.targetSdkVersion, app.info.seinfo, null); + app.info.targetSdkVersion, app.info.seinfo, requiredAbi, null); BatteryStatsImpl bs = mBatteryStatsService.getActiveStatistics(); synchronized (bs) { diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 49f29fe..63a52e6 100644..100755 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -942,8 +942,8 @@ final class ActivityRecord { // for another app to start, then we have paused dispatching // for this activity. ActivityRecord r = this; - final ActivityStack stack = task.stack; if (r.waitingVisible) { + final ActivityStack stack = mStackSupervisor.getFocusedStack(); // Hmmm, who might we be waiting for? r = stack.mResumedActivity; if (r == null) { diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java index 2c7f1f1..493e1e4 100644 --- a/services/java/com/android/server/am/NativeCrashListener.java +++ b/services/java/com/android/server/am/NativeCrashListener.java @@ -17,18 +17,18 @@ package com.android.server.am; import android.app.ApplicationErrorReport.CrashInfo; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructTimeval; +import android.system.StructUcred; import android.util.Slog; -import libcore.io.ErrnoException; -import libcore.io.Libcore; -import libcore.io.StructTimeval; -import libcore.io.StructUcred; - -import static libcore.io.OsConstants.*; +import static android.system.OsConstants.*; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileDescriptor; +import java.io.InterruptedIOException; import java.net.InetSocketAddress; import java.net.InetUnixAddress; @@ -76,7 +76,7 @@ final class NativeCrashListener extends Thread { try { CrashInfo ci = new CrashInfo(); ci.exceptionClassName = "Native crash"; - ci.exceptionMessage = Libcore.os.strsignal(mSignal); + ci.exceptionMessage = Os.strsignal(mSignal); ci.throwFileName = "unknown"; ci.throwClassName = "unknown"; ci.throwMethodName = "unknown"; @@ -116,22 +116,22 @@ final class NativeCrashListener extends Thread { } try { - FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0); + FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0); final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH); - Libcore.os.bind(serverFd, sockAddr, 0); - Libcore.os.listen(serverFd, 1); + Os.bind(serverFd, sockAddr, 0); + Os.listen(serverFd, 1); while (true) { InetSocketAddress peer = new InetSocketAddress(); FileDescriptor peerFd = null; try { if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection"); - peerFd = Libcore.os.accept(serverFd, peer); + peerFd = Os.accept(serverFd, peer); if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd); if (peerFd != null) { // Only the superuser is allowed to talk to us over this socket StructUcred credentials = - Libcore.os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED); + Os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED); if (credentials.uid == 0) { // the reporting thread may take responsibility for // acking the debugger; make sure we play along. @@ -145,7 +145,7 @@ final class NativeCrashListener extends Thread { // byte written is irrelevant. if (peerFd != null) { try { - Libcore.os.write(peerFd, ackSignal, 0, 1); + Os.write(peerFd, ackSignal, 0, 1); } catch (Exception e) { /* we don't care about failures here */ if (MORE_DEBUG) { @@ -153,7 +153,7 @@ final class NativeCrashListener extends Thread { } } try { - Libcore.os.close(peerFd); + Os.close(peerFd); } catch (ErrnoException e) { if (MORE_DEBUG) { Slog.d(TAG, "Exception closing socket: " + e.getMessage()); @@ -178,10 +178,10 @@ final class NativeCrashListener extends Thread { } static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes) - throws ErrnoException { + throws ErrnoException, InterruptedIOException { int totalRead = 0; while (numBytes > 0) { - int n = Libcore.os.read(fd, buffer, offset + totalRead, numBytes); + int n = Os.read(fd, buffer, offset + totalRead, numBytes); if (n <= 0) { if (DEBUG) { Slog.w(TAG, "Needed " + numBytes + " but saw " + n); @@ -202,8 +202,8 @@ final class NativeCrashListener extends Thread { try { StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS); - Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout); - Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout); + Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout); + Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout); // first, the pid and signal number int headerBytes = readExactly(fd, buf, 0, 8); @@ -237,7 +237,7 @@ final class NativeCrashListener extends Thread { int bytes; do { // get some data - bytes = Libcore.os.read(fd, buf, 0, buf.length); + bytes = Os.read(fd, buf, 0, buf.length); if (bytes > 0) { if (MORE_DEBUG) { String s = new String(buf, 0, bytes, "UTF-8"); diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index 09cb344..5305c8f 100644 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -75,42 +75,44 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final boolean localLOGV = false; private static final boolean REPORT_UNEXPECTED = false; private static final String TAG = "UsageStats"; - + // Current on-disk Parcel version private static final int VERSION = 1008; private static final int CHECKIN_VERSION = 4; - + private static final String FILE_PREFIX = "usage-"; private static final String FILE_HISTORY = FILE_PREFIX + "history.xml"; - private static final int FILE_WRITE_INTERVAL = 30*60*1000; //ms - + private static final int FILE_WRITE_INTERVAL = (localLOGV) ? 0 : 30*60*1000; // 30m in ms + private static final int MAX_NUM_FILES = 5; - + private static final int NUM_LAUNCH_TIME_BINS = 10; private static final int[] LAUNCH_TIME_BINS = { 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 5000 }; - + static IUsageStats sService; private Context mContext; // structure used to maintain statistics since the last checkin. - final private ArrayMap<String, PkgUsageStatsExtended> mStats; + final private ArrayMap<String, PkgUsageStatsExtended> mStats + = new ArrayMap<String, PkgUsageStatsExtended>(); // Maintains the last time any component was resumed, for all time. - final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes; + final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes + = new ArrayMap<String, ArrayMap<String, Long>>(); // To remove last-resume time stats when a pacakge is removed. private PackageMonitor mPackageMonitor; // Lock to update package stats. Methods suffixed by SLOCK should invoked with // this lock held - final Object mStatsLock; + final Object mStatsLock = new Object(); // Lock to write to file. Methods suffixed by FLOCK should invoked with // this lock held. - final Object mFileLock; + final Object mFileLock = new Object(); // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks private String mLastResumedPkg; private String mLastResumedComp; @@ -120,52 +122,53 @@ public final class UsageStatsService extends IUsageStats.Stub { private String mFileLeaf; private File mDir; - private Calendar mCal; // guarded by itself + private final Calendar mCal // guarded by itself + = Calendar.getInstance(TimeZone.getTimeZone("GMT+0")); private final AtomicInteger mLastWriteDay = new AtomicInteger(-1); private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0); private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false); - + static class TimeStats { - int count; - int[] times = new int[NUM_LAUNCH_TIME_BINS]; - + int mCount; + final int[] mTimes = new int[NUM_LAUNCH_TIME_BINS]; + TimeStats() { } - + void incCount() { - count++; + mCount++; } - + void add(int val) { final int[] bins = LAUNCH_TIME_BINS; for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) { if (val < bins[i]) { - times[i]++; + mTimes[i]++; return; } } - times[NUM_LAUNCH_TIME_BINS-1]++; + mTimes[NUM_LAUNCH_TIME_BINS-1]++; } - + TimeStats(Parcel in) { - count = in.readInt(); - final int[] localTimes = times; + mCount = in.readInt(); + final int[] localTimes = mTimes; for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { localTimes[i] = in.readInt(); } } - + void writeToParcel(Parcel out) { - out.writeInt(count); - final int[] localTimes = times; + out.writeInt(mCount); + final int[] localTimes = mTimes; for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { out.writeInt(localTimes[i]); } } } - - private class PkgUsageStatsExtended { + + static class PkgUsageStatsExtended { final ArrayMap<String, TimeStats> mLaunchTimes = new ArrayMap<String, TimeStats>(); final ArrayMap<String, TimeStats> mFullyDrawnTimes @@ -174,18 +177,18 @@ public final class UsageStatsService extends IUsageStats.Stub { long mUsageTime; long mPausedTime; long mResumedTime; - + PkgUsageStatsExtended() { mLaunchCount = 0; mUsageTime = 0; } - + PkgUsageStatsExtended(Parcel in) { mLaunchCount = in.readInt(); mUsageTime = in.readLong(); if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount + ", Usage time:" + mUsageTime); - + final int numLaunchTimeStats = in.readInt(); if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats); mLaunchTimes.ensureCapacity(numLaunchTimeStats); @@ -209,16 +212,16 @@ public final class UsageStatsService extends IUsageStats.Stub { void updateResume(String comp, boolean launched) { if (launched) { - mLaunchCount ++; + mLaunchCount++; } mResumedTime = SystemClock.elapsedRealtime(); } - + void updatePause() { mPausedTime = SystemClock.elapsedRealtime(); mUsageTime += (mPausedTime - mResumedTime); } - + void addLaunchCount(String comp) { TimeStats times = mLaunchTimes.get(comp); if (times == null) { @@ -227,7 +230,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } times.incCount(); } - + void addLaunchTime(String comp, int millis) { TimeStats times = mLaunchTimes.get(comp); if (times == null) { @@ -262,7 +265,7 @@ public final class UsageStatsService extends IUsageStats.Stub { mFullyDrawnTimes.valueAt(i).writeToParcel(out); } } - + void clear() { mLaunchTimes.clear(); mFullyDrawnTimes.clear(); @@ -270,32 +273,25 @@ public final class UsageStatsService extends IUsageStats.Stub { mUsageTime = 0; } } - + UsageStatsService(String dir) { - mStats = new ArrayMap<String, PkgUsageStatsExtended>(); - mLastResumeTimes = new ArrayMap<String, ArrayMap<String, Long>>(); - mStatsLock = new Object(); - mFileLock = new Object(); + if (localLOGV) Slog.v(TAG, "UsageStatsService: " + dir); mDir = new File(dir); - mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0")); - mDir.mkdir(); - - // Remove any old usage files from previous versions. + + // Remove any old /data/system/usagestats.* files from previous versions. File parentDir = mDir.getParentFile(); - String fList[] = parentDir.list(); - if (fList != null) { + String files[] = parentDir.list(); + if (files != null) { String prefix = mDir.getName() + "."; - int i = fList.length; - while (i > 0) { - i--; - if (fList[i].startsWith(prefix)) { - Slog.i(TAG, "Deleting old usage file: " + fList[i]); - (new File(parentDir, fList[i])).delete(); + for (String file : files) { + if (file.startsWith(prefix)) { + Slog.i(TAG, "Deleting old usage file: " + file); + (new File(parentDir, file)).delete(); } } } - + // Update current stats which are binned by date mFileLeaf = getCurrentDateStr(FILE_PREFIX); mFile = new File(mDir, mFileLeaf); @@ -312,11 +308,11 @@ public final class UsageStatsService extends IUsageStats.Stub { */ private String getCurrentDateStr(String prefix) { StringBuilder sb = new StringBuilder(); + if (prefix != null) { + sb.append(prefix); + } synchronized (mCal) { mCal.setTimeInMillis(System.currentTimeMillis()); - if (prefix != null) { - sb.append(prefix); - } sb.append(mCal.get(Calendar.YEAR)); int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1; if (mm < 10) { @@ -331,17 +327,20 @@ public final class UsageStatsService extends IUsageStats.Stub { } return sb.toString(); } - + private Parcel getParcelForFile(File file) throws IOException { FileInputStream stream = new FileInputStream(file); - byte[] raw = readFully(stream); - Parcel in = Parcel.obtain(); - in.unmarshall(raw, 0, raw.length); - in.setDataPosition(0); - stream.close(); - return in; + try { + byte[] raw = readFully(stream); + Parcel in = Parcel.obtain(); + in.unmarshall(raw, 0, raw.length); + in.setDataPosition(0); + return in; + } finally { + stream.close(); + } } - + private void readStatsFromFile() { File newFile = mFile; synchronized (mFileLock) { @@ -358,12 +357,13 @@ public final class UsageStatsService extends IUsageStats.Stub { } } } - + private void readStatsFLOCK(File file) throws IOException { Parcel in = getParcelForFile(file); int vers = in.readInt(); - if (vers != VERSION) { - Slog.w(TAG, "Usage stats version changed; dropping"); + if (vers != VERSION) { // vers will be 0 if the parcel file was empty + Slog.w(TAG, "Usage stats version of " + file + " changed from " + vers + " to " + + VERSION + "; dropping"); return; } int N = in.readInt(); @@ -384,12 +384,12 @@ public final class UsageStatsService extends IUsageStats.Stub { private void readHistoryStatsFromFile() { synchronized (mFileLock) { if (mHistoryFile.getBaseFile().exists()) { - readHistoryStatsFLOCK(mHistoryFile); + readHistoryStatsFLOCK(); } } } - private void readHistoryStatsFLOCK(AtomicFile file) { + private void readHistoryStatsFLOCK() { FileInputStream fis = null; try { fis = mHistoryFile.openRead(); @@ -472,12 +472,12 @@ public final class UsageStatsService extends IUsageStats.Stub { } return fileList; } - + private void checkFileLimitFLOCK() { // Get all usage stats output files ArrayList<String> fileList = getUsageStatsFileListFLOCK(); if (fileList == null) { - // Strange but we dont have to delete any thing + // Empty /data/system/usagestats/ so we don't have anything to delete return; } int count = fileList.size(); @@ -577,8 +577,8 @@ public final class UsageStatsService extends IUsageStats.Stub { } if (dayChanged || forceWriteHistoryStats) { - // Write history stats daily, or when forced (due to shutdown). - writeHistoryStatsFLOCK(mHistoryFile); + // Write history stats daily or when forced (due to shutdown) or when debugging. + writeHistoryStatsFLOCK(); } // Delete the backup file @@ -640,10 +640,10 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - private void writeHistoryStatsFLOCK(AtomicFile historyFile) { + private void writeHistoryStatsFLOCK() { FileOutputStream fos = null; try { - fos = historyFile.startWrite(); + fos = mHistoryFile.startWrite(); XmlSerializer out = new FastXmlSerializer(); out.setOutput(fos, "utf-8"); out.startDocument(null, true); @@ -666,11 +666,11 @@ public final class UsageStatsService extends IUsageStats.Stub { out.endTag(null, "usage-history"); out.endDocument(); - historyFile.finishWrite(fos); + mHistoryFile.finishWrite(fos); } catch (IOException e) { Slog.w(TAG,"Error writing history stats" + e); if (fos != null) { - historyFile.failWrite(fos); + mHistoryFile.failWrite(fos); } } } @@ -713,7 +713,8 @@ public final class UsageStatsService extends IUsageStats.Stub { sService = asInterface(b); return sService; } - + + @Override public void noteResumeComponent(ComponentName componentName) { enforceCallingPermission(); String pkgName; @@ -722,7 +723,7 @@ public final class UsageStatsService extends IUsageStats.Stub { ((pkgName = componentName.getPackageName()) == null)) { return; } - + final boolean samePackage = pkgName.equals(mLastResumedPkg); if (mIsResumed) { if (mLastResumedPkg != null) { @@ -736,14 +737,14 @@ public final class UsageStatsService extends IUsageStats.Stub { } } } - + final boolean sameComp = samePackage && componentName.getClassName().equals(mLastResumedComp); - + mIsResumed = true; mLastResumedPkg = pkgName; mLastResumedComp = componentName.getClassName(); - + if (localLOGV) Slog.i(TAG, "started component:" + pkgName); PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { @@ -764,9 +765,10 @@ public final class UsageStatsService extends IUsageStats.Stub { } } + @Override public void notePauseComponent(ComponentName componentName) { enforceCallingPermission(); - + synchronized (mStatsLock) { String pkgName; if ((componentName == null) || @@ -779,9 +781,9 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } mIsResumed = false; - + if (localLOGV) Slog.i(TAG, "paused component:"+pkgName); - + PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { // Weird some error here @@ -790,11 +792,12 @@ public final class UsageStatsService extends IUsageStats.Stub { } pus.updatePause(); } - + // Persist current data to file if needed. writeStatsToFile(false, false); } - + + @Override public void noteLaunchTime(ComponentName componentName, int millis) { enforceCallingPermission(); String pkgName; @@ -802,10 +805,10 @@ public final class UsageStatsService extends IUsageStats.Stub { ((pkgName = componentName.getPackageName()) == null)) { return; } - + // Persist current data to file if needed. writeStatsToFile(false, false); - + synchronized (mStatsLock) { PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus != null) { @@ -813,7 +816,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } } } - + public void noteFullyDrawnTime(ComponentName componentName, int millis) { enforceCallingPermission(); String pkgName; @@ -840,7 +843,8 @@ public final class UsageStatsService extends IUsageStats.Stub { mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, Binder.getCallingPid(), Binder.getCallingUid(), null); } - + + @Override public PkgUsageStats getPkgUsageStats(ComponentName componentName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); @@ -860,7 +864,8 @@ public final class UsageStatsService extends IUsageStats.Stub { return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes); } } - + + @Override public PkgUsageStats[] getAllPkgUsageStats() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); @@ -886,8 +891,8 @@ public final class UsageStatsService extends IUsageStats.Stub { return retArr; } } - - static byte[] readFully(FileInputStream stream) throws java.io.IOException { + + static byte[] readFully(FileInputStream stream) throws IOException { int pos = 0; int avail = stream.available(); byte[] data = new byte[avail]; @@ -905,7 +910,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } } } - + private void collectDumpInfoFLOCK(PrintWriter pw, boolean isCompactOutput, boolean deleteAfterPrint, HashSet<String> packages) { List<String> fileList = getUsageStatsFileListFLOCK(); @@ -934,15 +939,12 @@ public final class UsageStatsService extends IUsageStats.Stub { // Delete old file after collecting info only for checkin requests dFile.delete(); } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file); - return; } catch (IOException e) { Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file); - } + } } } - + private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw, String date, boolean isCompactOutput, HashSet<String> packages) { StringBuilder sb = new StringBuilder(512); @@ -953,19 +955,19 @@ public final class UsageStatsService extends IUsageStats.Stub { } else { sb.append("Date: "); } - + sb.append(date); - + int vers = in.readInt(); if (vers != VERSION) { sb.append(" (old data version)"); pw.println(sb.toString()); return; } - + pw.println(sb.toString()); int N = in.readInt(); - + while (N > 0) { N--; String pkgName = in.readString(); @@ -992,10 +994,10 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append(activity); TimeStats times = pus.mLaunchTimes.valueAt(i); sb.append(','); - sb.append(times.count); + sb.append(times.mCount); for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) { sb.append(","); - sb.append(times.times[j]); + sb.append(times.mTimes[j]); } sb.append('\n'); } @@ -1007,7 +1009,7 @@ public final class UsageStatsService extends IUsageStats.Stub { TimeStats times = pus.mFullyDrawnTimes.valueAt(i); for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) { sb.append(","); - sb.append(times.times[j]); + sb.append(times.mTimes[j]); } sb.append('\n'); } @@ -1027,26 +1029,26 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append(pus.mLaunchTimes.keyAt(i)); TimeStats times = pus.mLaunchTimes.valueAt(i); sb.append(": "); - sb.append(times.count); + sb.append(times.mCount); sb.append(" starts"); int lastBin = 0; for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) { - if (times.times[j] != 0) { + if (times.mTimes[j] != 0) { sb.append(", "); sb.append(lastBin); sb.append('-'); sb.append(LAUNCH_TIME_BINS[j]); sb.append("ms="); - sb.append(times.times[j]); + sb.append(times.mTimes[j]); } lastBin = LAUNCH_TIME_BINS[j]; } - if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) { + if (times.mTimes[NUM_LAUNCH_TIME_BINS-1] != 0) { sb.append(", "); sb.append(">="); sb.append(lastBin); sb.append("ms="); - sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]); + sb.append(times.mTimes[NUM_LAUNCH_TIME_BINS-1]); } sb.append('\n'); } @@ -1059,7 +1061,7 @@ public final class UsageStatsService extends IUsageStats.Stub { boolean needComma = false; int lastBin = 0; for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) { - if (times.times[j] != 0) { + if (times.mTimes[j] != 0) { if (needComma) { sb.append(", "); } else { @@ -1069,27 +1071,27 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append('-'); sb.append(LAUNCH_TIME_BINS[j]); sb.append("ms="); - sb.append(times.times[j]); + sb.append(times.mTimes[j]); } lastBin = LAUNCH_TIME_BINS[j]; } - if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) { + if (times.mTimes[NUM_LAUNCH_TIME_BINS-1] != 0) { if (needComma) { sb.append(", "); } sb.append(">="); sb.append(lastBin); sb.append("ms="); - sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]); + sb.append(times.mTimes[NUM_LAUNCH_TIME_BINS-1]); } sb.append('\n'); } } - + pw.write(sb.toString()); } } - + /** * Searches array of arguments for the specified string * @param args array of argument strings @@ -1106,7 +1108,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } return false; } - + /** * Searches array of arguments for the specified string's data * @param args array of argument strings @@ -1125,11 +1127,11 @@ public final class UsageStatsService extends IUsageStats.Stub { } return null; } - - @Override + /* - * The data persisted to file is parsed and the stats are computed. + * The data persisted to file is parsed and the stats are computed. */ + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -1143,23 +1145,23 @@ public final class UsageStatsService extends IUsageStats.Stub { final boolean isCompactOutput = isCheckinRequest || scanArgs(args, "-c"); final boolean deleteAfterPrint = isCheckinRequest || scanArgs(args, "-d"); final String rawPackages = scanArgsData(args, "--packages"); - + // Make sure the current stats are written to the file. This // doesn't need to be done if we are deleting files after printing, - // since it that case we won't print the current stats. + // since in that case we won't print the current stats. if (!deleteAfterPrint) { writeStatsToFile(true, false); } - + HashSet<String> packages = null; if (rawPackages != null) { if (!"*".equals(rawPackages)) { // A * is a wildcard to show all packages. String[] names = rawPackages.split(","); + if (names.length != 0) { + packages = new HashSet<String>(); + } for (String n : names) { - if (packages == null) { - packages = new HashSet<String>(); - } packages.add(n); } } @@ -1169,7 +1171,7 @@ public final class UsageStatsService extends IUsageStats.Stub { Slog.w(TAG, "Checkin without packages"); return; } - + synchronized (mFileLock) { collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages); } diff --git a/services/java/com/android/server/pm/BackgroundDexOptService.java b/services/java/com/android/server/pm/BackgroundDexOptService.java new file mode 100644 index 0000000..f2db791 --- /dev/null +++ b/services/java/com/android/server/pm/BackgroundDexOptService.java @@ -0,0 +1,91 @@ +/* + * 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.server.pm; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Log; + +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * {@hide} + */ +public class BackgroundDexOptService { + + static final String TAG = "BackgroundDexOptService"; + + private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) { + onIdleStart(); + } else if (Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) { + onIdleStop(); + } + } + }; + + final PackageManagerService mPackageManager; + + final AtomicBoolean mIdleTime = new AtomicBoolean(false); + + public BackgroundDexOptService(Context context) { + mPackageManager = (PackageManagerService)ServiceManager.getService("package"); + + IntentFilter idleMaintenanceFilter = new IntentFilter(); + idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START); + idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_END); + context.registerReceiverAsUser(mIdleMaintenanceReceiver, UserHandle.ALL, + idleMaintenanceFilter, null, null); + } + + public boolean onIdleStart() { + Log.i(TAG, "onIdleStart"); + if (mPackageManager.isStorageLow()) { + return false; + } + final HashSet<String> pkgs = mPackageManager.getPackagesThatNeedDexOpt(); + if (pkgs == null) { + return false; + } + mIdleTime.set(true); + new Thread("BackgroundDexOptService_DexOpter") { + @Override + public void run() { + for (String pkg : pkgs) { + if (!mIdleTime.get()) { + break; + } + mPackageManager.performDexOpt(pkg, false); + } + } + }.start(); + return true; + } + + public void onIdleStop() { + Log.i(TAG, "onIdleStop"); + mIdleTime.set(false); + } +} diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java index 4b087cc..b776ce1 100644 --- a/services/java/com/android/server/pm/Installer.java +++ b/services/java/com/android/server/pm/Installer.java @@ -201,7 +201,7 @@ public final class Installer { return execute(builder.toString()); } - public int dexopt(String apkPath, int uid, boolean isPublic) { + public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) { StringBuilder builder = new StringBuilder("dexopt"); builder.append(' '); builder.append(apkPath); @@ -209,10 +209,13 @@ public final class Installer { builder.append(uid); builder.append(isPublic ? " 1" : " 0"); builder.append(" *"); // No pkgName arg present + builder.append(' '); + builder.append(instructionSet); return execute(builder.toString()); } - public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName) { + public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName, + String instructionSet) { StringBuilder builder = new StringBuilder("dexopt"); builder.append(' '); builder.append(apkPath); @@ -221,6 +224,8 @@ public final class Installer { builder.append(isPublic ? " 1" : " 0"); builder.append(' '); builder.append(pkgName); + builder.append(' '); + builder.append(instructionSet); return execute(builder.toString()); } @@ -235,19 +240,23 @@ public final class Installer { return execute(builder.toString()); } - public int movedex(String srcPath, String dstPath) { + public int movedex(String srcPath, String dstPath, String instructionSet) { StringBuilder builder = new StringBuilder("movedex"); builder.append(' '); builder.append(srcPath); builder.append(' '); builder.append(dstPath); + builder.append(' '); + builder.append(instructionSet); return execute(builder.toString()); } - public int rmdex(String codePath) { + public int rmdex(String codePath, String instructionSet) { StringBuilder builder = new StringBuilder("rmdex"); builder.append(' '); builder.append(codePath); + builder.append(' '); + builder.append(instructionSet); return execute(builder.toString()); } @@ -334,7 +343,7 @@ public final class Installer { } public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath, - String fwdLockApkPath, String asecPath, PackageStats pStats) { + String fwdLockApkPath, String asecPath, String instructionSet, PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); @@ -348,6 +357,8 @@ public final class Installer { builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); builder.append(' '); builder.append(asecPath != null ? asecPath : "!"); + builder.append(' '); + builder.append(instructionSet); String s = transaction(builder.toString()); String res[] = s.split(" "); @@ -397,7 +408,14 @@ public final class Installer { return execute(builder.toString()); } - public boolean restoreconData() { - return (execute("restorecondata") == 0); + public boolean restoreconData(String pkgName, String seinfo, int uid) { + StringBuilder builder = new StringBuilder("restorecondata"); + builder.append(' '); + builder.append(pkgName); + builder.append(' '); + builder.append(seinfo != null ? seinfo : "!"); + builder.append(' '); + builder.append(uid); + return (execute(builder.toString()) == 0); } } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 5dded57..3275ab9 100755 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -23,14 +23,17 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.system.OsConstants.S_IRWXU; +import static android.system.OsConstants.S_IRGRP; +import static android.system.OsConstants.S_IXGRP; +import static android.system.OsConstants.S_IROTH; +import static android.system.OsConstants.S_IXOTH; +import static android.os.Process.PACKAGE_INFO_GID; +import static android.os.Process.SYSTEM_UID; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.ArrayUtils.removeInt; -import static libcore.io.OsConstants.S_IRWXU; -import static libcore.io.OsConstants.S_IRGRP; -import static libcore.io.OsConstants.S_IXGRP; -import static libcore.io.OsConstants.S_IROTH; -import static libcore.io.OsConstants.S_IXOTH; +import com.android.internal.R; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; @@ -41,8 +44,8 @@ import com.android.internal.util.XmlUtils; import com.android.server.DeviceStorageMonitorService; import com.android.server.EventLogTags; import com.android.server.IntentResolver; - import com.android.server.Watchdog; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -59,8 +62,8 @@ import android.content.IIntentReceiver; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; -import android.content.ServiceConnection; import android.content.IntentSender.SendIntentException; +import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ContainerEncryptionParams; @@ -72,14 +75,15 @@ import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.ManifestDigest; import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; -import android.content.pm.PackageParser; -import android.content.pm.PackageUserState; import android.content.pm.PackageParser.ActivityIntentInfo; +import android.content.pm.PackageParser; import android.content.pm.PackageStats; +import android.content.pm.PackageUserState; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; @@ -87,7 +91,6 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; -import android.content.pm.ManifestDigest; import android.content.pm.VerificationParams; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VerifierInfo; @@ -97,6 +100,7 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.Environment.UserEnvironment; import android.os.FileObserver; import android.os.FileUtils; import android.os.Handler; @@ -113,11 +117,14 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; -import android.os.Environment.UserEnvironment; import android.os.UserManager; import android.security.KeyStore; import android.security.SystemKeyStore; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStat; import android.text.TextUtils; +import android.util.AtomicFile; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -129,6 +136,7 @@ import android.util.Xml; import android.view.Display; import android.view.WindowManager; +import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; @@ -138,7 +146,9 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.cert.CertificateException; @@ -155,13 +165,13 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; -import libcore.io.ErrnoException; +import dalvik.system.DexFile; +import dalvik.system.StaleDexCacheError; +import dalvik.system.VMRuntime; import libcore.io.IoUtils; -import libcore.io.Libcore; -import libcore.io.StructStat; - -import com.android.internal.R; /** * Keep track of all those .apks everywhere. @@ -189,6 +199,7 @@ public class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_PACKAGE_SCANNING = false; private static final boolean DEBUG_APP_DIR_OBSERVER = false; private static final boolean DEBUG_VERIFY = false; + private static final boolean DEBUG_DEXOPT = false; private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; @@ -257,11 +268,14 @@ public class PackageManagerService extends IPackageManager.Stub { private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; private static final String LIB_DIR_NAME = "lib"; + private static final String LIB64_DIR_NAME = "lib64"; private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay"; static final String mTempContainerPrefix = "smdl2tmp"; + private static String sPreferredInstructionSet; + private static final String IDMAP_PREFIX = "/data/resource-cache/"; private static final String IDMAP_SUFFIX = "@idmap"; @@ -276,7 +290,6 @@ public class PackageManagerService extends IPackageManager.Stub { final Context mContext; final boolean mFactoryTest; final boolean mOnlyCore; - final boolean mNoDexOpt; final DisplayMetrics mMetrics; final int mDefParseFlags; final String[] mSeparateProcesses; @@ -393,6 +406,9 @@ public class PackageManagerService extends IPackageManager.Stub { // If mac_permissions.xml was found for seinfo labeling. boolean mFoundPolicyFile; + // If a recursive restorecon of /data/data/<pkg> is needed. + private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon(); + // All available activities, for your resolving pleasure. final ActivityIntentResolver mActivities = new ActivityIntentResolver(); @@ -577,6 +593,146 @@ public class PackageManagerService extends IPackageManager.Stub { private final String mRequiredVerifierPackage; + private final PackageUsage mPackageUsage = new PackageUsage(); + + private class PackageUsage { + private static final int WRITE_INTERVAL + = (DEBUG_DEXOPT) ? 0 : 30*60*1000; // 30m in ms + + private final Object mFileLock = new Object(); + private final AtomicLong mLastWritten = new AtomicLong(0); + private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false); + + private boolean mIsFirstBoot = false; + + boolean isFirstBoot() { + return mIsFirstBoot; + } + + void write(boolean force) { + if (force) { + writeInternal(); + return; + } + if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL + && !DEBUG_DEXOPT) { + return; + } + if (mBackgroundWriteRunning.compareAndSet(false, true)) { + new Thread("PackageUsage_DiskWriter") { + @Override + public void run() { + try { + writeInternal(); + } finally { + mBackgroundWriteRunning.set(false); + } + } + }.start(); + } + } + + private void writeInternal() { + synchronized (mPackages) { + synchronized (mFileLock) { + AtomicFile file = getFile(); + FileOutputStream f = null; + try { + f = file.startWrite(); + BufferedOutputStream out = new BufferedOutputStream(f); + FileUtils.setPermissions(file.getBaseFile().getPath(), 0660, SYSTEM_UID, PACKAGE_INFO_GID); + StringBuilder sb = new StringBuilder(); + for (PackageParser.Package pkg : mPackages.values()) { + if (pkg.mLastPackageUsageTimeInMills == 0) { + continue; + } + sb.setLength(0); + sb.append(pkg.packageName); + sb.append(' '); + sb.append((long)pkg.mLastPackageUsageTimeInMills); + sb.append('\n'); + out.write(sb.toString().getBytes(StandardCharsets.US_ASCII)); + } + out.flush(); + file.finishWrite(f); + } catch (IOException e) { + if (f != null) { + file.failWrite(f); + } + Log.e(TAG, "Failed to write package usage times", e); + } + } + } + mLastWritten.set(SystemClock.elapsedRealtime()); + } + + void readLP() { + synchronized (mFileLock) { + AtomicFile file = getFile(); + BufferedInputStream in = null; + try { + in = new BufferedInputStream(file.openRead()); + StringBuffer sb = new StringBuffer(); + while (true) { + String packageName = readToken(in, sb, ' '); + if (packageName == null) { + break; + } + String timeInMillisString = readToken(in, sb, '\n'); + if (timeInMillisString == null) { + throw new IOException("Failed to find last usage time for package " + + packageName); + } + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + continue; + } + long timeInMillis; + try { + timeInMillis = Long.parseLong(timeInMillisString.toString()); + } catch (NumberFormatException e) { + throw new IOException("Failed to parse " + timeInMillisString + + " as a long.", e); + } + pkg.mLastPackageUsageTimeInMills = timeInMillis; + } + } catch (FileNotFoundException expected) { + mIsFirstBoot = true; + } catch (IOException e) { + Log.w(TAG, "Failed to read package usage times", e); + } finally { + IoUtils.closeQuietly(in); + } + } + mLastWritten.set(SystemClock.elapsedRealtime()); + } + + private String readToken(InputStream in, StringBuffer sb, char endOfToken) + throws IOException { + sb.setLength(0); + while (true) { + int ch = in.read(); + if (ch == -1) { + if (sb.length() == 0) { + return null; + } + throw new IOException("Unexpected EOF"); + } + if (ch == endOfToken) { + return sb.toString(); + } + sb.append((char)ch); + } + } + + private AtomicFile getFile() { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + File fname = new File(systemDir, "package-usage.list"); + return new AtomicFile(fname); + } + } + class PackageHandler extends Handler { private boolean mBound = false; final ArrayList<HandlerParams> mPendingInstalls = @@ -1092,7 +1248,6 @@ public class PackageManagerService extends IPackageManager.Stub { mContext = context; mFactoryTest = factoryTest; mOnlyCore = onlyCore; - mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics(); mSettings = new Settings(context); mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, @@ -1174,10 +1329,6 @@ public class PackageManagerService extends IPackageManager.Stub { // Set flag to monitor and not change apk file paths when // scanning install directories. int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING; - if (mNoDexOpt) { - Slog.w(TAG, "Running ENG build: no pre-dexopt!"); - scanMode |= SCAN_NO_DEX; - } final HashSet<String> alreadyDexOpted = new HashSet<String>(); @@ -1196,29 +1347,39 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "No BOOTCLASSPATH found!"); } - boolean didDexOpt = false; + boolean didDexOptLibraryOrTool = false; + + final List<String> instructionSets = getAllInstructionSets(); /** * Ensure all external libraries have had dexopt run on them. */ if (mSharedLibraries.size() > 0) { - Iterator<SharedLibraryEntry> libs = mSharedLibraries.values().iterator(); - while (libs.hasNext()) { - String lib = libs.next().path; - if (lib == null) { - continue; - } - try { - if (dalvik.system.DexFile.isDexOptNeededInternal(lib, null, false)) { - alreadyDexOpted.add(lib); - mInstaller.dexopt(lib, Process.SYSTEM_UID, true); - didDexOpt = true; + // NOTE: For now, we're compiling these system "shared libraries" + // (and framework jars) into all available architectures. It's possible + // to compile them only when we come across an app that uses them (there's + // already logic for that in scanPackageLI) but that adds some complexity. + for (String instructionSet : instructionSets) { + for (SharedLibraryEntry libEntry : mSharedLibraries.values()) { + final String lib = libEntry.path; + if (lib == null) { + continue; + } + + try { + if (DexFile.isDexOptNeededInternal(lib, null, instructionSet, false)) { + alreadyDexOpted.add(lib); + + // The list of "shared libraries" we have at this point is + mInstaller.dexopt(lib, Process.SYSTEM_UID, true, instructionSet); + didDexOptLibraryOrTool = true; + } + } catch (FileNotFoundException e) { + Slog.w(TAG, "Library not found: " + lib); + } catch (IOException e) { + Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? " + + e.getMessage()); } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Library not found: " + lib); - } catch (IOException e) { - Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? " - + e.getMessage()); } } } @@ -1241,49 +1402,37 @@ public class PackageManagerService extends IPackageManager.Stub { */ String[] frameworkFiles = frameworkDir.list(); if (frameworkFiles != null) { - for (int i=0; i<frameworkFiles.length; i++) { - File libPath = new File(frameworkDir, frameworkFiles[i]); - String path = libPath.getPath(); - // Skip the file if we alrady did it. - if (alreadyDexOpted.contains(path)) { - continue; - } - // Skip the file if it is not a type we want to dexopt. - if (!path.endsWith(".apk") && !path.endsWith(".jar")) { - continue; - } - try { - if (dalvik.system.DexFile.isDexOptNeededInternal(path, null, false)) { - mInstaller.dexopt(path, Process.SYSTEM_UID, true); - didDexOpt = true; + // TODO: We could compile these only for the most preferred ABI. We should + // first double check that the dex files for these commands are not referenced + // by other system apps. + for (String instructionSet : instructionSets) { + for (int i=0; i<frameworkFiles.length; i++) { + File libPath = new File(frameworkDir, frameworkFiles[i]); + String path = libPath.getPath(); + // Skip the file if we already did it. + if (alreadyDexOpted.contains(path)) { + continue; + } + // Skip the file if it is not a type we want to dexopt. + if (!path.endsWith(".apk") && !path.endsWith(".jar")) { + continue; + } + try { + if (DexFile.isDexOptNeededInternal(path, null, instructionSet, false)) { + mInstaller.dexopt(path, Process.SYSTEM_UID, true, instructionSet); + didDexOptLibraryOrTool = true; + } + } catch (FileNotFoundException e) { + Slog.w(TAG, "Jar not found: " + path); + } catch (IOException e) { + Slog.w(TAG, "Exception reading jar: " + path, e); } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Jar not found: " + path); - } catch (IOException e) { - Slog.w(TAG, "Exception reading jar: " + path, e); } } } - if (didDexOpt) { - File dalvikCacheDir = new File(dataDir, "dalvik-cache"); - - // If we had to do a dexopt of one of the previous - // things, then something on the system has changed. - // Consider this significant, and wipe away all other - // existing dexopt files to ensure we don't leave any - // dangling around. - String[] files = dalvikCacheDir.list(); - if (files != null) { - for (int i=0; i<files.length; i++) { - String fn = files[i]; - if (fn.startsWith("data@app@") - || fn.startsWith("data@app-private@")) { - Slog.i(TAG, "Pruning dalvik file: " + fn); - (new File(dalvikCacheDir, fn)).delete(); - } - } - } + if (didDexOptLibraryOrTool) { + pruneDexFiles(new File(dataDir, "dalvik-cache")); } // Collect vendor overlay packages. @@ -1453,6 +1602,15 @@ public class PackageManagerService extends IPackageManager.Stub { // the correct library paths. updateAllSharedLibrariesLPw(); + for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) { + adjustCpuAbisForSharedUserLPw(setting.packages, true /* do dexopt */, + false /* force dexopt */, false /* defer dexopt */); + } + + // Now that we know all the packages we are keeping, + // read and update their last usage times. + mPackageUsage.readLP(); + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis()); Slog.i(TAG, "Time to scan packages: " @@ -1486,13 +1644,6 @@ public class PackageManagerService extends IPackageManager.Stub { // can downgrade to reader mSettings.writeLPr(); - if (SELinuxMMAC.shouldRestorecon()) { - Slog.i(TAG, "Relabeling of /data/data and /data/user issued."); - if (mInstaller.restoreconData()) { - SELinuxMMAC.setRestoreconDone(); - } - } - EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); @@ -1506,10 +1657,51 @@ public class PackageManagerService extends IPackageManager.Stub { } // synchronized (mInstallLock) } + private static void pruneDexFiles(File cacheDir) { + // If we had to do a dexopt of one of the previous + // things, then something on the system has changed. + // Consider this significant, and wipe away all other + // existing dexopt files to ensure we don't leave any + // dangling around. + // + // Additionally, delete all dex files from the root directory + // since there shouldn't be any there anyway. + // + // Note: This isn't as good an indicator as it used to be. It + // used to include the boot classpath but at some point + // DexFile.isDexOptNeeded started returning false for the boot + // class path files in all cases. It is very possible in a + // small maintenance release update that the library and tool + // jars may be unchanged but APK could be removed resulting in + // unused dalvik-cache files. + File[] files = cacheDir.listFiles(); + if (files != null) { + for (File file : files) { + if (!file.isDirectory()) { + Slog.i(TAG, "Pruning dalvik file: " + file.getAbsolutePath()); + file.delete(); + } else { + File[] subDirList = file.listFiles(); + if (subDirList != null) { + for (File subDirFile : subDirList) { + final String fn = subDirFile.getName(); + if (fn.startsWith("data@app@") || fn.startsWith("data@app-private@")) { + Slog.i(TAG, "Pruning dalvik file: " + fn); + subDirFile.delete(); + } + } + } + } + } + } + } + + @Override public boolean isFirstBoot() { - return !mRestoredSettings; + return !mRestoredSettings || mPackageUsage.isFirstBoot(); } + @Override public boolean isOnlyCoreApps() { return mOnlyCore; } @@ -1796,7 +1988,6 @@ public class PackageManagerService extends IPackageManager.Stub { PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) { if (!sUserManager.exists(userId)) return null; - PackageInfo pi; final PackageSetting ps = (PackageSetting) p.mExtras; if (ps == null) { return null; @@ -1808,6 +1999,7 @@ public class PackageManagerService extends IPackageManager.Stub { state, userId); } + @Override public boolean isPackageAvailable(String packageName, int userId) { if (!sUserManager.exists(userId)) return false; enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "is package available"); @@ -1845,6 +2037,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public String[] currentToCanonicalPackageNames(String[] names) { String[] out = new String[names.length]; // reader @@ -1857,6 +2050,7 @@ public class PackageManagerService extends IPackageManager.Stub { return out; } + @Override public String[] canonicalToCurrentPackageNames(String[] names) { String[] out = new String[names.length]; // reader @@ -1917,6 +2111,7 @@ public class PackageManagerService extends IPackageManager.Stub { return pi; } + @Override public PermissionInfo getPermissionInfo(String name, int flags) { // reader synchronized (mPackages) { @@ -1928,6 +2123,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) { // reader synchronized (mPackages) { @@ -1951,6 +2147,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) { // reader synchronized (mPackages) { @@ -1959,6 +2156,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public List<PermissionGroupInfo> getAllPermissionGroups(int flags) { // reader synchronized (mPackages) { @@ -2009,6 +2207,7 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.dataDir = getDataPathForPackage(packageName, 0).getPath(); pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; + pkg.applicationInfo.cpuAbi = ps.cpuAbiString; } return generatePackageInfo(pkg, flags, userId); } @@ -2043,6 +2242,7 @@ public class PackageManagerService extends IPackageManager.Stub { } + @Override public void freeStorageAndNotify(final long freeStorageSize, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); @@ -2068,6 +2268,7 @@ public class PackageManagerService extends IPackageManager.Stub { }); } + @Override public void freeStorage(final long freeStorageSize, final IntentSender pi) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); @@ -2171,6 +2372,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public String[] getSystemSharedLibraryNames() { Set<String> libSet; synchronized (mPackages) { @@ -2185,6 +2387,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public FeatureInfo[] getSystemAvailableFeatures() { Collection<FeatureInfo> featSet; synchronized (mPackages) { @@ -2203,6 +2406,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public boolean hasSystemFeature(String name) { synchronized (mPackages) { return mAvailableFeatures.containsKey(name); @@ -2217,6 +2421,7 @@ public class PackageManagerService extends IPackageManager.Stub { + " is not privileged to communicate with user=" + userId); } + @Override public int checkPermission(String permName, String pkgName) { synchronized (mPackages) { PackageParser.Package p = mPackages.get(pkgName); @@ -2234,6 +2439,7 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.PERMISSION_DENIED; } + @Override public int checkUidPermission(String permName, int uid) { synchronized (mPackages) { Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); @@ -2379,18 +2585,21 @@ public class PackageManagerService extends IPackageManager.Stub { return added; } + @Override public boolean addPermission(PermissionInfo info) { synchronized (mPackages) { return addPermissionLocked(info, false); } } + @Override public boolean addPermissionAsync(PermissionInfo info) { synchronized (mPackages) { return addPermissionLocked(info, true); } } + @Override public void removePermission(String name) { synchronized (mPackages) { checkPermissionTreeLP(name); @@ -2435,6 +2644,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void grantPermission(String packageName, String permissionName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null); @@ -2464,6 +2674,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void revokePermission(String packageName, String permissionName) { int changedAppId = -1; @@ -2522,12 +2733,14 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public boolean isProtectedBroadcast(String actionName) { synchronized (mPackages) { return mProtectedBroadcasts.contains(actionName); } } + @Override public int checkSignatures(String pkg1, String pkg2) { synchronized (mPackages) { final PackageParser.Package p1 = mPackages.get(pkg1); @@ -2540,6 +2753,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public int checkUidSignatures(int uid1, int uid2) { // Map to base uids. uid1 = UserHandle.getAppId(uid1); @@ -2600,6 +2814,7 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.SIGNATURE_NO_MATCH; } + @Override public String[] getPackagesForUid(int uid) { uid = UserHandle.getAppId(uid); // reader @@ -2623,6 +2838,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public String getNameForUid(int uid) { // reader synchronized (mPackages) { @@ -2638,6 +2854,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public int getUidForSharedUser(String sharedUserName) { if(sharedUserName == null) { return -1; @@ -2652,6 +2869,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public int getFlagsForUid(int uid) { synchronized (mPackages) { Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); @@ -3436,6 +3654,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) { ArrayList<ProviderInfo> finalList = null; @@ -3473,6 +3692,7 @@ public class PackageManagerService extends IPackageManager.Stub { return finalList; } + @Override public InstrumentationInfo getInstrumentationInfo(ComponentName name, int flags) { // reader @@ -3482,6 +3702,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public List<InstrumentationInfo> queryInstrumentation(String targetPackage, int flags) { ArrayList<InstrumentationInfo> finalList = @@ -3729,7 +3950,8 @@ public class PackageManagerService extends IPackageManager.Stub { + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps), - ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); + ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, + getAppInstructionSetFromSettings(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } @@ -3793,7 +4015,8 @@ public class PackageManagerService extends IPackageManager.Stub { + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps), - ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); + ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, + getAppInstructionSetFromSettings(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } @@ -3906,56 +4129,133 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void performBootDexOpt() { - HashSet<PackageParser.Package> pkgs = null; + enforceSystemOrRoot("Only the system can request dexopt be performed"); + + final HashSet<PackageParser.Package> pkgs; synchronized (mPackages) { pkgs = mDeferredDexOpt; mDeferredDexOpt = null; } + if (pkgs != null) { + // Filter out packages that aren't recently used. + // + // The exception is first boot of a non-eng device, which + // should do a full dexopt. + boolean eng = "eng".equals(SystemProperties.get("ro.build.type")); + if (eng || !isFirstBoot()) { + // TODO: add a property to control this? + long dexOptLRUThresholdInMinutes; + if (eng) { + dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds. + } else { + dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users. + } + long dexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000; + + int total = pkgs.size(); + int skipped = 0; + long now = System.currentTimeMillis(); + for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) { + PackageParser.Package pkg = i.next(); + long then = pkg.mLastPackageUsageTimeInMills; + if (then + dexOptLRUThresholdInMills < now) { + if (DEBUG_DEXOPT) { + Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " + + ((then == 0) ? "never" : new Date(then))); + } + i.remove(); + skipped++; + } + } + if (DEBUG_DEXOPT) { + Log.i(TAG, "Skipped optimizing " + skipped + " of " + total); + } + } + int i = 0; for (PackageParser.Package pkg : pkgs) { + i++; + if (DEBUG_DEXOPT) { + Log.i(TAG, "Optimizing app " + i + " of " + pkgs.size() + + ": " + pkg.packageName); + } if (!isFirstBoot()) { - i++; try { ActivityManagerNative.getDefault().showBootMessage( mContext.getResources().getString( - com.android.internal.R.string.android_upgrading_apk, + R.string.android_upgrading_apk, i, pkgs.size()), true); } catch (RemoteException e) { } } PackageParser.Package p = pkg; synchronized (mInstallLock) { - if (!p.mDidDexOpt) { - performDexOptLI(p, false, false, true); + if (p.mDexOptNeeded) { + performDexOptLI(p, false /* force dex */, false /* defer */, + true /* include dependencies */); } } } } } + @Override public boolean performDexOpt(String packageName) { enforceSystemOrRoot("Only the system can request dexopt be performed"); + return performDexOpt(packageName, true); + } - if (!mNoDexOpt) { - return false; - } + public boolean performDexOpt(String packageName, boolean updateUsage) { PackageParser.Package p; synchronized (mPackages) { p = mPackages.get(packageName); - if (p == null || p.mDidDexOpt) { + if (p == null) { + return false; + } + if (updateUsage) { + p.mLastPackageUsageTimeInMills = System.currentTimeMillis(); + } + mPackageUsage.write(false); + if (!p.mDexOptNeeded) { return false; } } + synchronized (mInstallLock) { - return performDexOptLI(p, false, false, true) == DEX_OPT_PERFORMED; + return performDexOptLI(p, false /* force dex */, false /* defer */, + true /* include dependencies */) == DEX_OPT_PERFORMED; } } - private void performDexOptLibsLI(ArrayList<String> libs, boolean forceDex, boolean defer, - HashSet<String> done) { + public HashSet<String> getPackagesThatNeedDexOpt() { + HashSet<String> pkgs = null; + synchronized (mPackages) { + for (PackageParser.Package p : mPackages.values()) { + if (DEBUG_DEXOPT) { + Log.i(TAG, p.packageName + " mDexOptNeeded=" + p.mDexOptNeeded); + } + if (!p.mDexOptNeeded) { + continue; + } + if (pkgs == null) { + pkgs = new HashSet<String>(); + } + pkgs.add(p.packageName); + } + } + return pkgs; + } + + public void shutdown() { + mPackageUsage.write(true); + } + + private void performDexOptLibsLI(ArrayList<String> libs, String instructionSet, + boolean forceDex, boolean defer, HashSet<String> done) { for (int i=0; i<libs.size(); i++) { PackageParser.Package libPkg; String libName; @@ -3969,7 +4269,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } if (libPkg != null && !done.contains(libName)) { - performDexOptLI(libPkg, forceDex, defer, done); + performDexOptLI(libPkg, instructionSet, forceDex, defer, done); } } } @@ -3979,72 +4279,124 @@ public class PackageManagerService extends IPackageManager.Stub { static final int DEX_OPT_DEFERRED = 2; static final int DEX_OPT_FAILED = -1; - private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer, - HashSet<String> done) { - boolean performed = false; + private int performDexOptLI(PackageParser.Package pkg, String instructionSetOverride, + boolean forceDex, boolean defer, HashSet<String> done) { + final String instructionSet = instructionSetOverride != null ? + instructionSetOverride : getAppInstructionSet(pkg.applicationInfo); + if (done != null) { done.add(pkg.packageName); if (pkg.usesLibraries != null) { - performDexOptLibsLI(pkg.usesLibraries, forceDex, defer, done); + performDexOptLibsLI(pkg.usesLibraries, instructionSet, forceDex, defer, done); } if (pkg.usesOptionalLibraries != null) { - performDexOptLibsLI(pkg.usesOptionalLibraries, forceDex, defer, done); + performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSet, forceDex, defer, done); } } + + boolean performed = false; if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { String path = pkg.mScanPath; - int ret = 0; try { - if (forceDex || dalvik.system.DexFile.isDexOptNeededInternal(path, pkg.packageName, - defer)) { - if (!forceDex && defer) { - if (mDeferredDexOpt == null) { - mDeferredDexOpt = new HashSet<PackageParser.Package>(); - } - mDeferredDexOpt.add(pkg); - return DEX_OPT_DEFERRED; - } else { - Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); - final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); - ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), - pkg.packageName); - pkg.mDidDexOpt = true; - performed = true; - } - } + boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path, + pkg.packageName, + instructionSet, + defer); + // There are three basic cases here: + // 1.) we need to dexopt, either because we are forced or it is needed + // 2.) we are defering a needed dexopt + // 3.) we are skipping an unneeded dexopt + if (forceDex || (!defer && isDexOptNeededInternal)) { + Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); + final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); + int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), + pkg.packageName, instructionSet); + // Note that we ran dexopt, since rerunning will + // probably just result in an error again. + pkg.mDexOptNeeded = false; + if (ret < 0) { + return DEX_OPT_FAILED; + } + return DEX_OPT_PERFORMED; + } + if (defer && isDexOptNeededInternal) { + if (mDeferredDexOpt == null) { + mDeferredDexOpt = new HashSet<PackageParser.Package>(); + } + mDeferredDexOpt.add(pkg); + return DEX_OPT_DEFERRED; + } + pkg.mDexOptNeeded = false; + return DEX_OPT_SKIPPED; } catch (FileNotFoundException e) { Slog.w(TAG, "Apk not found for dexopt: " + path); - ret = -1; + return DEX_OPT_FAILED; } catch (IOException e) { Slog.w(TAG, "IOException reading apk: " + path, e); - ret = -1; - } catch (dalvik.system.StaleDexCacheError e) { + return DEX_OPT_FAILED; + } catch (StaleDexCacheError e) { Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e); - ret = -1; + return DEX_OPT_FAILED; } catch (Exception e) { Slog.w(TAG, "Exception when doing dexopt : ", e); - ret = -1; - } - if (ret < 0) { - //error from installer return DEX_OPT_FAILED; } } + return DEX_OPT_SKIPPED; + } + + private String getAppInstructionSet(ApplicationInfo info) { + String instructionSet = getPreferredInstructionSet(); - return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED; + if (info.cpuAbi != null) { + instructionSet = VMRuntime.getInstructionSet(info.cpuAbi); + } + + return instructionSet; + } + + private String getAppInstructionSetFromSettings(PackageSetting ps) { + String instructionSet = getPreferredInstructionSet(); + + if (ps.cpuAbiString != null) { + instructionSet = VMRuntime.getInstructionSet(ps.cpuAbiString); + } + + return instructionSet; + } + + private static String getPreferredInstructionSet() { + if (sPreferredInstructionSet == null) { + sPreferredInstructionSet = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]); + } + + return sPreferredInstructionSet; + } + + private static List<String> getAllInstructionSets() { + final String[] allAbis = Build.SUPPORTED_ABIS; + final List<String> allInstructionSets = new ArrayList<String>(allAbis.length); + + for (String abi : allAbis) { + final String instructionSet = VMRuntime.getInstructionSet(abi); + if (!allInstructionSets.contains(instructionSet)) { + allInstructionSets.add(instructionSet); + } + } + + return allInstructionSets; } private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer, boolean inclDependencies) { HashSet<String> done; - boolean performed = false; if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) { done = new HashSet<String>(); done.add(pkg.packageName); } else { done = null; } - return performDexOptLI(pkg, forceDex, defer, done); + return performDexOptLI(pkg, null /* instruction set override */, forceDex, defer, done); } private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { @@ -4148,6 +4500,14 @@ public class PackageManagerService extends IPackageManager.Stub { private boolean updateSharedLibrariesLPw(PackageParser.Package pkg, PackageParser.Package changingLib) { + // We might be upgrading from a version of the platform that did not + // provide per-package native library directories for system apps. + // Fix that up here. + if (isSystemApp(pkg)) { + PackageSetting ps = mSettings.mPackages.get(pkg.applicationInfo.packageName); + setInternalAppNativeLibraryPath(pkg, ps); + } + if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) { if (mTmpSharedLibraries == null || mTmpSharedLibraries.length < mSharedLibraries.size()) { @@ -4272,7 +4632,7 @@ public class PackageManagerService extends IPackageManager.Stub { mResolveActivity.processName = "system:ui"; mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; - mResolveActivity.theme = com.android.internal.R.style.Theme_Holo_Dialog_Alert; + mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert; mResolveActivity.exported = true; mResolveActivity.enabled = true; mResolveInfo.activityInfo = mResolveActivity; @@ -4314,17 +4674,6 @@ public class PackageManagerService extends IPackageManager.Stub { // writer synchronized (mPackages) { - if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { - // Check all shared libraries and map to their actual file path. - // We only do this here for apps not on a system dir, because those - // are the only ones that can fail an install due to this. We - // will take care of the system apps by updating all of their - // library paths after the scan is done. - if (!updateSharedLibrariesLPw(pkg, null)) { - return null; - } - } - if (pkg.mSharedUserId != null) { suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, true); if (suid == null) { @@ -4399,6 +4748,7 @@ public class PackageManagerService extends IPackageManager.Stub { // the PkgSetting exists already and doesn't have to be created. pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryDir, + pkg.applicationInfo.cpuAbi, pkg.applicationInfo.flags, user, false); if (pkgSetting == null) { Slog.w(TAG, "Creating application package " + pkg.packageName + " failed"); @@ -4434,6 +4784,17 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } + if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { + // Check all shared libraries and map to their actual file path. + // We only do this here for apps not on a system dir, because those + // are the only ones that can fail an install due to this. We + // will take care of the system apps by updating all of their + // library paths after the scan is done. + if (!updateSharedLibrariesLPw(pkg, null)) { + return null; + } + } + if (mFoundPolicyFile) { SELinuxMMAC.assignSeinfoValue(pkg); } @@ -4534,7 +4895,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (dataPath.exists()) { int currentUid = 0; try { - StructStat stat = Libcore.os.stat(dataPath.getPath()); + StructStat stat = Os.stat(dataPath.getPath()); currentUid = stat.st_uid; } catch (ErrnoException e) { Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e); @@ -4617,6 +4978,11 @@ public class PackageManagerService extends IPackageManager.Stub { } } pkg.applicationInfo.dataDir = dataPath.getPath(); + if (mShouldRestoreconData) { + Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued."); + mInstaller.restoreconData(pkg.packageName, pkg.applicationInfo.seinfo, + pkg.applicationInfo.uid); + } } else { if (DEBUG_PACKAGE_SCANNING) { if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) @@ -4655,7 +5021,6 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString; } } - pkgSetting.uidError = uidError; } @@ -4687,6 +5052,8 @@ public class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "removed obsolete native libraries for system package " + path); } + + setInternalAppAbi(pkg, pkgSetting); } else { if (!isForwardLocked(pkg) && !isExternal(pkg)) { /* @@ -4699,16 +5066,47 @@ public class PackageManagerService extends IPackageManager.Stub { } try { - if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) { + int copyRet = copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir); + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { Slog.e(TAG, "Unable to copy native libraries"); mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; return null; } + + // We've successfully copied native libraries across, so we make a + // note of what ABI we're using + if (copyRet != PackageManager.NO_NATIVE_LIBRARIES) { + pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[copyRet]; + } else { + pkg.applicationInfo.cpuAbi = null; + } } catch (IOException e) { Slog.e(TAG, "Unable to copy native libraries", e); mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; return null; } + } else { + // We don't have to copy the shared libraries if we're in the ASEC container + // but we still need to scan the file to figure out what ABI the app needs. + // + // TODO: This duplicates work done in the default container service. It's possible + // to clean this up but we'll need to change the interface between this service + // and IMediaContainerService (but doing so will spread this logic out, rather + // than centralizing it). + final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); + final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); + if (abi >= 0) { + pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[abi]; + } else if (abi == PackageManager.NO_NATIVE_LIBRARIES) { + // Note that (non upgraded) system apps will not have any native + // libraries bundled in their APK, but we're guaranteed not to be + // such an app at this point. + pkg.applicationInfo.cpuAbi = null; + } else { + mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + return null; + } + handle.close(); } if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path); @@ -4781,8 +5179,7 @@ public class PackageManagerService extends IPackageManager.Stub { } if (allowed) { if (!mSharedLibraries.containsKey(name)) { - mSharedLibraries.put(name, new SharedLibraryEntry(null, - pkg.packageName)); + mSharedLibraries.put(name, new SharedLibraryEntry(null, pkg.packageName)); } else if (!name.equals(pkg.packageName)) { Slog.w(TAG, "Package " + pkg.packageName + " library " + name + " already exists; skipping"); @@ -4851,6 +5248,12 @@ public class PackageManagerService extends IPackageManager.Stub { // writer synchronized (mPackages) { + if ((scanMode&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) { + // We don't do this here during boot because we can do it all + // at once after scanning all existing packages. + adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, + true, forceDex, (scanMode & SCAN_DEFER_DEX) != 0); + } // We don't expect installation to fail beyond this point, if ((scanMode&SCAN_MONITOR) != 0) { mAppDirs.put(pkg.mPath, pkg); @@ -5194,6 +5597,55 @@ public class PackageManagerService extends IPackageManager.Stub { return pkg; } + public void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser, + boolean doDexOpt, boolean forceDexOpt, boolean deferDexOpt) { + String requiredInstructionSet = null; + PackageSetting requirer = null; + for (PackageSetting ps : packagesForUser) { + if (ps.cpuAbiString != null) { + final String instructionSet = VMRuntime.getInstructionSet(ps.cpuAbiString); + if (requiredInstructionSet != null) { + if (!instructionSet.equals(requiredInstructionSet)) { + // We have a mismatch between instruction sets (say arm vs arm64). + // + // TODO: We should rescan all the packages in a shared UID to check if + // they do contain shared libs for other ABIs in addition to the ones we've + // already extracted. For example, the package might contain both arm64-v8a + // and armeabi-v7a shared libs, and we'd have chosen arm64-v8a on 64 bit + // devices. + String errorMessage = "Instruction set mismatch, " + requirer.pkg.packageName + + " requires " + requiredInstructionSet + " whereas " + ps.pkg.packageName + + " requires " + instructionSet; + Slog.e(TAG, errorMessage); + + reportSettingsProblem(Log.WARN, errorMessage); + // Give up, don't bother making any other changes to the package settings. + return; + } + } else { + requiredInstructionSet = instructionSet; + requirer = ps; + } + } + } + + if (requiredInstructionSet != null) { + for (PackageSetting ps : packagesForUser) { + if (ps.cpuAbiString == null) { + ps.cpuAbiString = requirer.cpuAbiString; + if (ps.pkg != null) { + ps.pkg.applicationInfo.cpuAbi = requirer.cpuAbiString; + Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + ps.cpuAbiString); + if (doDexOpt) { + performDexOptLI(ps.pkg, forceDexOpt, deferDexOpt, true); + mInstaller.rmdex(ps.codePathString, getPreferredInstructionSet()); + } + } + } + } + } + } + private void setUpCustomResolverActivity(PackageParser.Package pkg) { synchronized (mPackages) { mResolverReplaced = true; @@ -5218,14 +5670,86 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private String calculateApkRoot(final String codePathString) { + final File codePath = new File(codePathString); + final File codeRoot; + if (FileUtils.contains(Environment.getRootDirectory(), codePath)) { + codeRoot = Environment.getRootDirectory(); + } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) { + codeRoot = Environment.getOemDirectory(); + } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) { + codeRoot = Environment.getVendorDirectory(); + } else { + // Unrecognized code path; take its top real segment as the apk root: + // e.g. /something/app/blah.apk => /something + try { + File f = codePath.getCanonicalFile(); + File parent = f.getParentFile(); // non-null because codePath is a file + File tmp; + while ((tmp = parent.getParentFile()) != null) { + f = parent; + parent = tmp; + } + codeRoot = f; + Slog.w(TAG, "Unrecognized code path " + + codePath + " - using " + codeRoot); + } catch (IOException e) { + // Can't canonicalize the lib path -- shenanigans? + Slog.w(TAG, "Can't canonicalize code path " + codePath); + return Environment.getRootDirectory().getPath(); + } + } + return codeRoot.getPath(); + } + + // This is the initial scan-time determination of how to handle a given + // package for purposes of native library location. private void setInternalAppNativeLibraryPath(PackageParser.Package pkg, PackageSetting pkgSetting) { - final String apkLibPath = getApkName(pkgSetting.codePathString); - final String nativeLibraryPath = new File(mAppLibInstallDir, apkLibPath).getPath(); + // "bundled" here means system-installed with no overriding update + final boolean bundledApk = isSystemApp(pkg) && !isUpdatedSystemApp(pkg); + final String apkName = getApkName(pkg.applicationInfo.sourceDir); + final File libDir; + if (bundledApk) { + // If "/system/lib64/apkname" exists, assume that is the per-package + // native library directory to use; otherwise use "/system/lib/apkname". + String apkRoot = calculateApkRoot(pkg.applicationInfo.sourceDir); + File lib64 = new File(apkRoot, LIB64_DIR_NAME); + File packLib64 = new File(lib64, apkName); + libDir = (packLib64.exists()) ? lib64 : new File(apkRoot, LIB_DIR_NAME); + } else { + libDir = mAppLibInstallDir; + } + final String nativeLibraryPath = (new File(libDir, apkName)).getPath(); pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath; pkgSetting.nativeLibraryPathString = nativeLibraryPath; } + // Deduces the required ABI of an upgraded system app. + private void setInternalAppAbi(PackageParser.Package pkg, PackageSetting pkgSetting) { + final String apkRoot = calculateApkRoot(pkg.applicationInfo.sourceDir); + final String apkName = getApkName(pkg.applicationInfo.sourceDir); + + // This is of the form "/system/lib64/<packagename>", "/vendor/lib64/<packagename>" + // or similar. + final File lib64 = new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath()); + final File lib = new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath()); + + // Assume that the bundled native libraries always correspond to the + // most preferred 32 or 64 bit ABI. + if (lib64.exists()) { + pkg.applicationInfo.cpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + pkgSetting.cpuAbiString = Build.SUPPORTED_64_BIT_ABIS[0]; + } else if (lib.exists()) { + pkg.applicationInfo.cpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + pkgSetting.cpuAbiString = Build.SUPPORTED_32_BIT_ABIS[0]; + } else { + // This is the case where the app has no native code. + pkg.applicationInfo.cpuAbi = null; + pkgSetting.cpuAbiString = null; + } + } + private static int copyNativeLibrariesForInternalApp(File scanFile, final File nativeLibraryDir) throws IOException { if (!nativeLibraryDir.isDirectory()) { @@ -5236,8 +5760,7 @@ public class PackageManagerService extends IPackageManager.Stub { } try { - Libcore.os.chmod(nativeLibraryDir.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH - | S_IXOTH); + Os.chmod(nativeLibraryDir.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); } catch (ErrnoException e) { throw new IOException("Cannot chmod native library directory " + nativeLibraryDir.getPath(), e); @@ -5250,7 +5773,21 @@ public class PackageManagerService extends IPackageManager.Stub { * If this is an internal application or our nativeLibraryPath points to * the app-lib directory, unpack the libraries if necessary. */ - return NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir); + final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); + try { + int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); + if (abi >= 0) { + int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, + nativeLibraryDir, Build.SUPPORTED_ABIS[abi]); + if (copyRet != PackageManager.INSTALL_SUCCEEDED) { + return copyRet; + } + } + + return abi; + } finally { + handle.close(); + } } private void killApplication(String pkgName, int appId, String reason) { @@ -6440,6 +6977,7 @@ public class PackageManagerService extends IPackageManager.Stub { return mMediaMounted || Environment.isExternalStorageEmulated(); } + @Override public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) { // writer synchronized (mPackages) { @@ -6619,6 +7157,7 @@ public class PackageManagerService extends IPackageManager.Stub { } /* Called when a downloaded package installation has been confirmed by the user */ + @Override public void installPackage( final Uri packageURI, final IPackageInstallObserver observer, final int flags, final String installerPackageName) { @@ -6636,6 +7175,7 @@ public class PackageManagerService extends IPackageManager.Stub { installerPackageName, verificationParams, encryptionParams); } + @Override public void installPackageWithVerificationAndEncryption(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { @@ -7002,6 +7542,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void finishPackageInstall(int token) { enforceSystemOrRoot("Only the system is allowed to finish installs"); @@ -7073,6 +7614,7 @@ public class PackageManagerService extends IPackageManager.Stub { -1); } + @Override public void setInstallerPackageName(String targetPackage, String installerPackageName) { final int uid = Binder.getCallingUid(); // writer @@ -7790,7 +8332,8 @@ public class PackageManagerService extends IPackageManager.Stub { int mRet; MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags, - String packageName, String dataDir, int uid, UserHandle user) { + String packageName, String dataDir, String instructionSet, + int uid, UserHandle user) { super(user); this.srcArgs = srcArgs; this.observer = observer; @@ -7799,7 +8342,7 @@ public class PackageManagerService extends IPackageManager.Stub { this.uid = uid; if (srcArgs != null) { Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath())); - targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir); + targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir, instructionSet); } else { targetArgs = null; } @@ -7908,7 +8451,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath, - String nativeLibraryPath) { + String nativeLibraryPath, String instructionSet) { final boolean isInAsec; if (installOnSd(flags)) { /* Apps on SD card are always in ASEC containers. */ @@ -7926,21 +8469,23 @@ public class PackageManagerService extends IPackageManager.Stub { if (isInAsec) { return new AsecInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath, - installOnSd(flags), installForwardLocked(flags)); + instructionSet, installOnSd(flags), installForwardLocked(flags)); } else { - return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath); + return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath, + instructionSet); } } // Used by package mover - private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir) { + private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir, + String instructionSet) { if (installOnSd(flags) || installForwardLocked(flags)) { String cid = getNextCodePath(packageURI.getPath(), pkgName, "/" + AsecInstallArgs.RES_FILE_NAME); - return new AsecInstallArgs(packageURI, cid, installOnSd(flags), + return new AsecInstallArgs(packageURI, cid, instructionSet, installOnSd(flags), installForwardLocked(flags)); } else { - return new FileInstallArgs(packageURI, pkgName, dataDir); + return new FileInstallArgs(packageURI, pkgName, dataDir, instructionSet); } } @@ -7952,16 +8497,18 @@ public class PackageManagerService extends IPackageManager.Stub { final String installerPackageName; final ManifestDigest manifestDigest; final UserHandle user; + final String instructionSet; InstallArgs(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, ManifestDigest manifestDigest, - UserHandle user) { + UserHandle user, String instructionSet) { this.packageURI = packageURI; this.flags = flags; this.observer = observer; this.installerPackageName = installerPackageName; this.manifestDigest = manifestDigest; this.user = user; + this.instructionSet = instructionSet; } abstract void createCopyFile(); @@ -8017,11 +8564,12 @@ public class PackageManagerService extends IPackageManager.Stub { FileInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.flags, params.installerPackageName, params.getManifestDigest(), - params.getUser()); + params.getUser(), null /* instruction set */); } - FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) { - super(null, null, 0, null, null, null); + FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, + String instructionSet) { + super(null, null, 0, null, null, null, instructionSet); File codeFile = new File(fullCodePath); installDir = codeFile.getParentFile(); codeFileName = fullCodePath; @@ -8029,8 +8577,8 @@ public class PackageManagerService extends IPackageManager.Stub { libraryPath = nativeLibraryPath; } - FileInstallArgs(Uri packageURI, String pkgName, String dataDir) { - super(packageURI, null, 0, null, null, null); + FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) { + super(packageURI, null, 0, null, null, null, instructionSet); installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; String apkName = getNextCodePath(null, pkgName, ".apk"); codeFileName = new File(installDir, apkName + ".apk").getPath(); @@ -8136,7 +8684,7 @@ public class PackageManagerService extends IPackageManager.Stub { } try { int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile); - if (copyRet != PackageManager.INSTALL_SUCCEEDED) { + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { return copyRet; } } catch (IOException e) { @@ -8289,7 +8837,10 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanUpResourcesLI() { String sourceDir = getCodePath(); if (cleanUp()) { - int retCode = mInstaller.rmdex(sourceDir); + if (instructionSet == null) { + throw new IllegalStateException("instructionSet == null"); + } + int retCode = mInstaller.rmdex(sourceDir, instructionSet); if (retCode < 0) { Slog.w(TAG, "Couldn't remove dex file for package: " + " at location " @@ -8353,14 +8904,14 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.flags, params.installerPackageName, params.getManifestDigest(), - params.getUser()); + params.getUser(), null /* instruction set */); } AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, - boolean isExternal, boolean isForwardLocked) { + String instructionSet, boolean isExternal, boolean isForwardLocked) { super(null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null); + null, null, null, instructionSet); // Extract cid from fullCodePath int eidx = fullCodePath.lastIndexOf("/"); String subStr1 = fullCodePath.substring(0, eidx); @@ -8369,18 +8920,19 @@ public class PackageManagerService extends IPackageManager.Stub { setCachePath(subStr1); } - AsecInstallArgs(String cid, boolean isForwardLocked) { + AsecInstallArgs(String cid, String instructionSet, boolean isForwardLocked) { super(null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null); + null, null, null, instructionSet); this.cid = cid; setCachePath(PackageHelper.getSdDir(cid)); } - AsecInstallArgs(Uri packageURI, String cid, boolean isExternal, boolean isForwardLocked) { + AsecInstallArgs(Uri packageURI, String cid, String instructionSet, + boolean isExternal, boolean isForwardLocked) { super(packageURI, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null); + null, null, null, instructionSet); this.cid = cid; } @@ -8561,7 +9113,10 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanUpResourcesLI() { String sourceFile = getCodePath(); // Remove dex file - int retCode = mInstaller.rmdex(sourceFile); + if (instructionSet == null) { + throw new IllegalStateException("instructionSet == null"); + } + int retCode = mInstaller.rmdex(sourceFile, instructionSet); if (retCode < 0) { Slog.w(TAG, "Couldn't remove dex file for package: " + " at location " @@ -8942,7 +9497,8 @@ public class PackageManagerService extends IPackageManager.Stub { res.removedInfo.args = createInstallArgs(0, deletedPackage.applicationInfo.sourceDir, deletedPackage.applicationInfo.publicSourceDir, - deletedPackage.applicationInfo.nativeLibraryDir); + deletedPackage.applicationInfo.nativeLibraryDir, + getAppInstructionSet(deletedPackage.applicationInfo)); } else { res.removedInfo.args = null; } @@ -8989,21 +9545,22 @@ public class PackageManagerService extends IPackageManager.Stub { // Utility method used to move dex files during install. private int moveDexFilesLI(PackageParser.Package newPackage) { - int retCode; if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath); + final String instructionSet = getAppInstructionSet(newPackage.applicationInfo); + int retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath, + instructionSet); if (retCode != 0) { - if (mNoDexOpt) { - /* - * If we're in an engineering build, programs are lazily run - * through dexopt. If the .dex file doesn't exist yet, it - * will be created when the program is run next. - */ - Slog.i(TAG, "dex file doesn't exist, skipping move: " + newPackage.mPath); - } else { - Slog.e(TAG, "Couldn't rename dex file: " + newPackage.mPath); - return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - } + /* + * Programs may be lazily run through dexopt, so the + * source may not exist. However, something seems to + * have gone wrong, so note that dexopt needs to be + * run again and remove the source file. In addition, + * remove the target to make sure there isn't a stale + * file from a previous version of the package. + */ + newPackage.mDexOptNeeded = true; + mInstaller.rmdex(newPackage.mScanPath, instructionSet); + mInstaller.rmdex(newPackage.mPath, instructionSet); } } return PackageManager.INSTALL_SUCCEEDED; @@ -9615,13 +10172,14 @@ public class PackageManagerService extends IPackageManager.Stub { } // writer synchronized (mPackages) { + PackageSetting ps = mSettings.mPackages.get(newPkg.packageName); + setInternalAppNativeLibraryPath(newPkg, ps); updatePermissionsLPw(newPkg.packageName, newPkg, UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG); if (applyUserRestrictions) { if (DEBUG_REMOVE) { Slog.d(TAG, "Propagating install state across reinstall"); } - PackageSetting ps = mSettings.mPackages.get(newPkg.packageName); for (int i = 0; i < allUserHandles.length; i++) { if (DEBUG_REMOVE) { Slog.d(TAG, " user " + allUserHandles[i] @@ -9655,7 +10213,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Delete application code and resources if (deleteCodeAndResources && (outInfo != null)) { outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString, - ps.resourcePathString, ps.nativeLibraryPathString); + ps.resourcePathString, ps.nativeLibraryPathString, + getAppInstructionSetFromSettings(ps)); } return true; } @@ -9944,6 +10503,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void deleteApplicationCacheFiles(final String packageName, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( @@ -9996,6 +10556,7 @@ public class PackageManagerService extends IPackageManager.Stub { return true; } + @Override public void getPackageSizeInfo(final String packageName, int userHandle, final IPackageStatsObserver observer) { mContext.enforceCallingOrSelfPermission( @@ -10022,9 +10583,10 @@ public class PackageManagerService extends IPackageManager.Stub { boolean dataOnly = false; String libDirPath = null; String asecPath = null; + PackageSetting ps = null; synchronized (mPackages) { p = mPackages.get(packageName); - PackageSetting ps = mSettings.mPackages.get(packageName); + ps = mSettings.mPackages.get(packageName); if(p == null) { dataOnly = true; if((ps == null) || (ps.pkg == null)) { @@ -10055,7 +10617,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, libDirPath, - publicSrcDir, asecPath, pStats); + publicSrcDir, asecPath, getAppInstructionSetFromSettings(ps), + pStats); if (res < 0) { return false; } @@ -10070,14 +10633,17 @@ public class PackageManagerService extends IPackageManager.Stub { } + @Override public void addPackageToPreferred(String packageName) { Slog.w(TAG, "addPackageToPreferred: this is now a no-op"); } + @Override public void removePackageFromPreferred(String packageName) { Slog.w(TAG, "removePackageFromPreferred: this is now a no-op"); } + @Override public List<PackageInfo> getPreferredPackages(int flags) { return new ArrayList<PackageInfo>(); } @@ -10105,6 +10671,7 @@ public class PackageManagerService extends IPackageManager.Stub { return Build.VERSION_CODES.CUR_DEVELOPMENT; } + @Override public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, int userId) { addPreferredActivityInternal(filter, match, set, activity, true, userId); @@ -10141,6 +10708,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { if (filter.countActions() != 1) { @@ -10197,6 +10765,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void clearPackagePreferredActivities(String packageName) { final int uid = Binder.getCallingUid(); // writer @@ -10260,6 +10829,7 @@ public class PackageManagerService extends IPackageManager.Stub { return changed; } + @Override public void resetPreferredActivities(int userId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); @@ -10273,6 +10843,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public int getPreferredActivities(List<IntentFilter> outFilters, List<ComponentName> outActivities, String packageName) { @@ -10484,6 +11055,7 @@ public class PackageManagerService extends IPackageManager.Stub { new int[] {UserHandle.getUserId(packageUid)}); } + @Override public void setPackageStoppedState(String packageName, boolean stopped, int userId) { if (!sUserManager.exists(userId)) return; final int uid = Binder.getCallingUid(); @@ -10500,6 +11072,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public String getInstallerPackageName(String packageName) { // reader synchronized (mPackages) { @@ -10529,6 +11102,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void enterSafeMode() { enforceSystemOrRoot("Only the system can request entering safe mode"); @@ -10537,6 +11111,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void systemReady() { mSystemReady = true; @@ -10582,10 +11157,12 @@ public class PackageManagerService extends IPackageManager.Stub { sUserManager.systemReady(); } + @Override public boolean isSafeMode() { return mSafeMode; } + @Override public boolean hasSystemUidErrors() { return mHasSystemUidErrors; } @@ -11025,6 +11602,7 @@ public class PackageManagerService extends IPackageManager.Stub { /* * Update media status on PackageManager. */ + @Override public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) { int callingUid = Binder.getCallingUid(); if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { @@ -11062,6 +11640,10 @@ public class PackageManagerService extends IPackageManager.Stub { */ public void scanAvailableAsecs() { updateExternalMediaStatusInner(true, false, false); + if (mShouldRestoreconData) { + SELinuxMMAC.setRestoreconDone(); + mShouldRestoreconData = false; + } } /* @@ -11117,7 +11699,9 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } - final AsecInstallArgs args = new AsecInstallArgs(cid, isForwardLocked(ps)); + final AsecInstallArgs args = new AsecInstallArgs(cid, + getAppInstructionSetFromSettings(ps), + isForwardLocked(ps)); // The package status is changed only if the code path // matches between settings and the container id. if (ps.codePathString != null && ps.codePathString.equals(args.getCodePath())) { @@ -11432,15 +12016,17 @@ public class PackageManagerService extends IPackageManager.Stub { * anyway. */ if (returnCode != PackageManager.MOVE_SUCCEEDED) { - processPendingMove(new MoveParams(null, observer, 0, packageName, + processPendingMove(new MoveParams(null, observer, 0, packageName, null, null, -1, user), returnCode); } else { Message msg = mHandler.obtainMessage(INIT_COPY); + final String instructionSet = getAppInstructionSet(pkg.applicationInfo); InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir, - pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir); + pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir, + instructionSet); MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName, - pkg.applicationInfo.dataDir, pkg.applicationInfo.uid, user); + pkg.applicationInfo.dataDir, instructionSet, pkg.applicationInfo.uid, user); msg.obj = mp; mHandler.sendMessage(msg); } @@ -11507,8 +12093,17 @@ public class PackageManagerService extends IPackageManager.Stub { final File newNativeDir = new File(newNativePath); if (!isForwardLocked(pkg) && !isExternal(pkg)) { - NativeLibraryHelper.copyNativeBinariesIfNeededLI( - new File(newCodePath), newNativeDir); + // NOTE: We do not report any errors from the APK scan and library + // copy at this point. + NativeLibraryHelper.ApkHandle handle = + new NativeLibraryHelper.ApkHandle(newCodePath); + final int abi = NativeLibraryHelper.findSupportedAbi( + handle, Build.SUPPORTED_ABIS); + if (abi >= 0) { + NativeLibraryHelper.copyNativeBinariesIfNeededLI( + handle, newNativeDir, Build.SUPPORTED_ABIS[abi]); + } + handle.close(); } final int[] users = sUserManager.getUserIds(); for (int user : users) { @@ -11599,6 +12194,7 @@ public class PackageManagerService extends IPackageManager.Stub { }); } + @Override public boolean setInstallLocation(int loc) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS, null); @@ -11614,6 +12210,7 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } + @Override public int getInstallLocation() { return android.provider.Settings.Global.getInt(mContext.getContentResolver(), android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, @@ -11685,6 +12282,7 @@ public class PackageManagerService extends IPackageManager.Stub { return true; } + @Override public boolean isStorageLow() { final long token = Binder.clearCallingIdentity(); try { diff --git a/services/java/com/android/server/pm/PackageSetting.java b/services/java/com/android/server/pm/PackageSetting.java index b447861..284da99 100644 --- a/services/java/com/android/server/pm/PackageSetting.java +++ b/services/java/com/android/server/pm/PackageSetting.java @@ -30,8 +30,8 @@ final class PackageSetting extends PackageSettingBase { SharedUserSetting sharedUser; PackageSetting(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, int pVersionCode, int pkgFlags) { - super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode, + String nativeLibraryPathString, String cpuAbiString, int pVersionCode, int pkgFlags) { + super(name, realName, codePath, resourcePath, nativeLibraryPathString, cpuAbiString, pVersionCode, pkgFlags); } diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java index 7747c8f..44803a5 100644 --- a/services/java/com/android/server/pm/PackageSettingBase.java +++ b/services/java/com/android/server/pm/PackageSettingBase.java @@ -53,6 +53,7 @@ class PackageSettingBase extends GrantedPermissions { File resourcePath; String resourcePathString; String nativeLibraryPathString; + String cpuAbiString; long timeStamp; long firstInstallTime; long lastUpdateTime; @@ -80,11 +81,11 @@ class PackageSettingBase extends GrantedPermissions { /* package name of the app that installed this package */ String installerPackageName; PackageSettingBase(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, int pVersionCode, int pkgFlags) { + String nativeLibraryPathString, String cpuAbiString, int pVersionCode, int pkgFlags) { super(pkgFlags); this.name = name; this.realName = realName; - init(codePath, resourcePath, nativeLibraryPathString, pVersionCode); + init(codePath, resourcePath, nativeLibraryPathString, cpuAbiString, pVersionCode); } /** @@ -101,6 +102,7 @@ class PackageSettingBase extends GrantedPermissions { resourcePath = base.resourcePath; resourcePathString = base.resourcePathString; nativeLibraryPathString = base.nativeLibraryPathString; + cpuAbiString = base.cpuAbiString; timeStamp = base.timeStamp; firstInstallTime = base.firstInstallTime; lastUpdateTime = base.lastUpdateTime; @@ -128,12 +130,13 @@ class PackageSettingBase extends GrantedPermissions { } void init(File codePath, File resourcePath, String nativeLibraryPathString, - int pVersionCode) { + String requiredCpuAbiString, int pVersionCode) { this.codePath = codePath; this.codePathString = codePath.toString(); this.resourcePath = resourcePath; this.resourcePathString = resourcePath.toString(); this.nativeLibraryPathString = nativeLibraryPathString; + this.cpuAbiString = requiredCpuAbiString; this.versionCode = pVersionCode; } @@ -164,6 +167,7 @@ class PackageSettingBase extends GrantedPermissions { grantedPermissions = base.grantedPermissions; gids = base.gids; + cpuAbiString = base.cpuAbiString; timeStamp = base.timeStamp; firstInstallTime = base.firstInstallTime; lastUpdateTime = base.lastUpdateTime; diff --git a/services/java/com/android/server/pm/PendingPackage.java b/services/java/com/android/server/pm/PendingPackage.java index c17cc46..36c3a34 100644 --- a/services/java/com/android/server/pm/PendingPackage.java +++ b/services/java/com/android/server/pm/PendingPackage.java @@ -22,8 +22,8 @@ final class PendingPackage extends PackageSettingBase { final int sharedId; PendingPackage(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) { - super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode, + String nativeLibraryPathString, String requiredCpuAbiString, int sharedId, int pVersionCode, int pkgFlags) { + super(name, realName, codePath, resourcePath, nativeLibraryPathString, requiredCpuAbiString, pVersionCode, pkgFlags); this.sharedId = sharedId; } diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java index af7153f..d70c725 100644 --- a/services/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/java/com/android/server/pm/SELinuxMMAC.java @@ -28,7 +28,6 @@ import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index ed025e1..14f38fb 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -36,6 +36,7 @@ import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; import com.android.server.pm.PackageManagerService.DumpState; +import java.util.Collection; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -213,10 +214,10 @@ final class Settings { PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, int pkgFlags, UserHandle user, boolean add) { + String nativeLibraryPathString, String cpuAbiString, int pkgFlags, UserHandle user, boolean add) { final String name = pkg.packageName; PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath, - resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, + resourcePath, nativeLibraryPathString, cpuAbiString, pkg.mVersionCode, pkgFlags, user, add, true /* allowInstall */); return p; } @@ -262,6 +263,11 @@ final class Settings { return s; } + Collection<SharedUserSetting> getAllSharedUsersLPw() { + return mSharedUsers.values(); + } + + boolean disableSystemPackageLPw(String name) { final PackageSetting p = mPackages.get(name); if(p == null) { @@ -298,7 +304,7 @@ final class Settings { p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath, - p.nativeLibraryPathString, p.appId, p.versionCode, p.pkgFlags); + p.nativeLibraryPathString, p.cpuAbiString, p.appId, p.versionCode, p.pkgFlags); mDisabledSysPackages.remove(name); return ret; } @@ -312,7 +318,7 @@ final class Settings { } PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, int uid, int vc, int pkgFlags) { + String nativeLibraryPathString, String cpuAbiString, int uid, int vc, int pkgFlags) { PackageSetting p = mPackages.get(name); if (p != null) { if (p.appId == uid) { @@ -322,7 +328,7 @@ final class Settings { "Adding duplicate package, keeping first: " + name); return null; } - p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString, + p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString, cpuAbiString, vc, pkgFlags); p.appId = uid; if (addUserIdLPw(uid, p, name)) { @@ -391,10 +397,11 @@ final class Settings { private PackageSetting getPackageLPw(String name, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, int vc, int pkgFlags, + String nativeLibraryPathString, String cpuAbiString, int vc, int pkgFlags, UserHandle installUser, boolean add, boolean allowInstall) { PackageSetting p = mPackages.get(name); if (p != null) { + p.cpuAbiString = cpuAbiString; if (!p.codePath.equals(codePath)) { // Check to see if its a disabled system app if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { @@ -438,7 +445,7 @@ final class Settings { if (origPackage != null) { // We are consuming the data from an existing package. p = new PackageSetting(origPackage.name, name, codePath, resourcePath, - nativeLibraryPathString, vc, pkgFlags); + nativeLibraryPathString, cpuAbiString, vc, pkgFlags); if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + name + " is adopting original package " + origPackage.name); // Note that we will retain the new package's signature so @@ -455,7 +462,7 @@ final class Settings { p.setTimeStamp(codePath.lastModified()); } else { p = new PackageSetting(name, realName, codePath, resourcePath, - nativeLibraryPathString, vc, pkgFlags); + nativeLibraryPathString, cpuAbiString, vc, pkgFlags); p.setTimeStamp(codePath.lastModified()); p.sharedUser = sharedUser; // If this is not a system app, it starts out stopped. @@ -581,6 +588,8 @@ final class Settings { && !nativeLibraryPath.equalsIgnoreCase(p.nativeLibraryPathString)) { p.nativeLibraryPathString = nativeLibraryPath; } + // Update the required Cpu Abi + p.cpuAbiString = pkg.applicationInfo.cpuAbi; // Update version code if needed if (pkg.mVersionCode != p.versionCode) { p.versionCode = pkg.mVersionCode; @@ -1498,6 +1507,9 @@ final class Settings { if (pkg.nativeLibraryPathString != null) { serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString); } + if (pkg.cpuAbiString != null) { + serializer.attribute(null, "requiredCpuAbi", pkg.cpuAbiString); + } if (pkg.sharedUser == null) { serializer.attribute(null, "userId", Integer.toString(pkg.appId)); } else { @@ -1540,6 +1552,9 @@ final class Settings { if (pkg.nativeLibraryPathString != null) { serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString); } + if (pkg.cpuAbiString != null) { + serializer.attribute(null, "requiredCpuAbi", pkg.cpuAbiString); + } serializer.attribute(null, "flags", Integer.toString(pkg.pkgFlags)); serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp)); serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime)); @@ -1804,7 +1819,7 @@ final class Settings { if (idObj != null && idObj instanceof SharedUserSetting) { PackageSetting p = getPackageLPw(pp.name, null, pp.realName, (SharedUserSetting) idObj, pp.codePath, pp.resourcePath, - pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, + pp.nativeLibraryPathString, pp.cpuAbiString, pp.versionCode, pp.pkgFlags, null, true /* add */, false /* allowInstall */); if (p == null) { PackageManagerService.reportSettingsProblem(Log.WARN, @@ -2224,6 +2239,8 @@ final class Settings { String codePathStr = parser.getAttributeValue(null, "codePath"); String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); + String cpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); + if (resourcePathStr == null) { resourcePathStr = codePathStr; } @@ -2243,7 +2260,7 @@ final class Settings { pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED; } PackageSetting ps = new PackageSetting(name, realName, codePathFile, - new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags); + new File(resourcePathStr), nativeLibraryPathStr, cpuAbiString, versionCode, pkgFlags); String timeStampStr = parser.getAttributeValue(null, "ft"); if (timeStampStr != null) { try { @@ -2310,6 +2327,7 @@ final class Settings { String codePathStr = null; String resourcePathStr = null; String nativeLibraryPathStr = null; + String cpuAbiString = null; String systemStr = null; String installerPackageName = null; String uidError = null; @@ -2329,6 +2347,8 @@ final class Settings { codePathStr = parser.getAttributeValue(null, "codePath"); resourcePathStr = parser.getAttributeValue(null, "resourcePath"); nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); + cpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); + version = parser.getAttributeValue(null, "version"); if (version != null) { try { @@ -2405,7 +2425,7 @@ final class Settings { + parser.getPositionDescription()); } else if (userId > 0) { packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr), - new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode, + new File(resourcePathStr), nativeLibraryPathStr, cpuAbiString, userId, versionCode, pkgFlags); if (PackageManagerService.DEBUG_SETTINGS) Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId=" @@ -2423,7 +2443,7 @@ final class Settings { userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; if (userId > 0) { packageSetting = new PendingPackage(name.intern(), realName, new File( - codePathStr), new File(resourcePathStr), nativeLibraryPathStr, userId, + codePathStr), new File(resourcePathStr), nativeLibraryPathStr, cpuAbiString, userId, versionCode, pkgFlags); packageSetting.setTimeStamp(timeStamp); packageSetting.firstInstallTime = firstInstallTime; @@ -2452,6 +2472,7 @@ final class Settings { packageSetting.uidError = "true".equals(uidError); packageSetting.installerPackageName = installerPackageName; packageSetting.nativeLibraryPathString = nativeLibraryPathStr; + packageSetting.cpuAbiString = cpuAbiString; // Handle legacy string here for single-user mode final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); if (enabledStr != null) { @@ -2915,6 +2936,7 @@ final class Settings { pw.print(prefix); pw.print(" codePath="); pw.println(ps.codePathString); pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.resourcePathString); pw.print(prefix); pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString); + pw.print(prefix); pw.print(" requiredCpuAbi="); pw.println(ps.cpuAbiString); pw.print(prefix); pw.print(" versionCode="); pw.print(ps.versionCode); if (ps.pkg != null) { pw.print(" targetSdk="); pw.print(ps.pkg.applicationInfo.targetSdkVersion); diff --git a/services/java/com/android/server/power/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java index 88a27f5..126d4c0 100644 --- a/services/java/com/android/server/power/ShutdownThread.java +++ b/services/java/com/android/server/power/ShutdownThread.java @@ -44,6 +44,7 @@ import android.os.storage.IMountService; import android.os.storage.IMountShutdownObserver; import com.android.internal.telephony.ITelephony; +import com.android.server.pm.PackageManagerService; import android.util.Log; import android.view.WindowManager; @@ -328,6 +329,14 @@ public final class ShutdownThread extends Thread { } } + Log.i(TAG, "Shutting down package manager..."); + + final PackageManagerService pm = (PackageManagerService) + ServiceManager.getService("package"); + if (pm != null) { + pm.shutdown(); + } + // Shutdown radios. shutdownRadios(MAX_RADIO_WAIT_TIME); diff --git a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java index e430814..3c960c7 100644 --- a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java +++ b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java @@ -18,10 +18,10 @@ package com.android.server.updates; import android.content.Context; import android.content.Intent; -import android.os.FileUtils; -import android.os.SELinux; import android.os.SystemProperties; import android.provider.Settings; +import android.system.ErrnoException; +import android.system.Os; import android.util.Base64; import android.util.Slog; @@ -30,9 +30,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { @@ -112,16 +110,16 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { File update = new File(updateDir.getParentFile(), "update"); File tmp = new File(updateDir.getParentFile(), "tmp"); if (current.exists()) { - Libcore.os.symlink(updateDir.getPath(), update.getPath()); - Libcore.os.rename(update.getPath(), current.getPath()); + Os.symlink(updateDir.getPath(), update.getPath()); + Os.rename(update.getPath(), current.getPath()); } else { - Libcore.os.symlink(updateDir.getPath(), current.getPath()); + Os.symlink(updateDir.getPath(), current.getPath()); } contexts.mkdirs(); backupContexts(contexts); copyUpdate(contexts); - Libcore.os.symlink(contexts.getPath(), tmp.getPath()); - Libcore.os.rename(tmp.getPath(), current.getPath()); + Os.symlink(contexts.getPath(), tmp.getPath()); + Os.rename(tmp.getPath(), current.getPath()); SystemProperties.set("selinux.reload_policy", "1"); } diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index 5a60de0..5f07517 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -573,14 +573,19 @@ public class UsbDeviceManager { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra("state", (enabled ? 1 : 0)); if (enabled) { + Scanner scanner = null; try { - Scanner scanner = new Scanner(new File(AUDIO_SOURCE_PCM_PATH)); + scanner = new Scanner(new File(AUDIO_SOURCE_PCM_PATH)); int card = scanner.nextInt(); int device = scanner.nextInt(); intent.putExtra("card", card); intent.putExtra("device", device); } catch (FileNotFoundException e) { Slog.e(TAG, "could not open audio source PCM file", e); + } finally { + if (scanner != null) { + scanner.close(); + } } } mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 606fcb4..1ee390f 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -797,6 +797,8 @@ public class ServiceState implements Parcelable { return TelephonyManager.NETWORK_TYPE_LTE; case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP: return TelephonyManager.NETWORK_TYPE_HSPAP; + case ServiceState.RIL_RADIO_TECHNOLOGY_GSM: + return TelephonyManager.NETWORK_TYPE_GSM; default: return TelephonyManager.NETWORK_TYPE_UNKNOWN; } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index ea22bc4..612f327 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -872,6 +872,8 @@ public class TelephonyManager { public static final int NETWORK_TYPE_EHRPD = 14; /** Current network is HSPA+ */ public static final int NETWORK_TYPE_HSPAP = 15; + /** Current network is GSM {@hide} */ + public static final int NETWORK_TYPE_GSM = 16; /** * @return the NETWORK_TYPE_xxxx for current data connection. @@ -963,6 +965,7 @@ public class TelephonyManager { public static int getNetworkClass(int networkType) { switch (networkType) { case NETWORK_TYPE_GPRS: + case NETWORK_TYPE_GSM: case NETWORK_TYPE_EDGE: case NETWORK_TYPE_CDMA: case NETWORK_TYPE_1xRTT: @@ -1029,6 +1032,8 @@ public class TelephonyManager { return "iDEN"; case NETWORK_TYPE_HSPAP: return "HSPA+"; + case NETWORK_TYPE_GSM: + return "GSM"; default: return "UNKNOWN"; } @@ -1055,6 +1060,10 @@ public class TelephonyManager { public static final int SIM_STATE_NETWORK_LOCKED = 4; /** SIM card state: Ready */ public static final int SIM_STATE_READY = 5; + /** SIM card state: SIM Card Error, Sim Card is present but faulty + *@hide + */ + public static final int SIM_STATE_CARD_IO_ERROR = 6; /** * @return true if a ICC card is present @@ -1081,6 +1090,7 @@ public class TelephonyManager { * @see #SIM_STATE_PUK_REQUIRED * @see #SIM_STATE_NETWORK_LOCKED * @see #SIM_STATE_READY + * @see #SIM_STATE_CARD_IO_ERROR */ public int getSimState() { String prop = SystemProperties.get(TelephonyProperties.PROPERTY_SIM_STATE); @@ -1099,6 +1109,9 @@ public class TelephonyManager { else if ("READY".equals(prop)) { return SIM_STATE_READY; } + else if ("CARD_IO_ERROR".equals(prop)) { + return SIM_STATE_CARD_IO_ERROR; + } else { return SIM_STATE_UNKNOWN; } diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java index 236bb2f..8029713 100644 --- a/telephony/java/com/android/internal/telephony/IccCardConstants.java +++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java @@ -28,6 +28,8 @@ public class IccCardConstants { public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY"; /* ABSENT means ICC is missing */ public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT"; + /* CARD_IO_ERROR means for three consecutive times there was SIM IO error */ + static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR"; /* LOCKED means ICC is locked by pin or by network */ public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED"; /* READY means ICC is ready to access */ @@ -63,7 +65,8 @@ public class IccCardConstants { NETWORK_LOCKED, READY, NOT_READY, - PERM_DISABLED; + PERM_DISABLED, + CARD_IO_ERROR; public boolean isPinLocked() { return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)); @@ -72,7 +75,7 @@ public class IccCardConstants { public boolean iccCardExist() { return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED) || (this == NETWORK_LOCKED) || (this == READY) - || (this == PERM_DISABLED)); + || (this == PERM_DISABLED) || (this == CARD_IO_ERROR)); } } } diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index 5089b9d..a6f2442 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -42,6 +42,15 @@ typedef enum Command { } Command; /* + * Pseudolocalization methods + */ +typedef enum PseudolocalizationMethod { + NO_PSEUDOLOCALIZATION = 0, + PSEUDO_ACCENTED, + PSEUDO_BIDI, +} PseudolocalizationMethod; + +/* * Bundle of goodies, including everything specified on the command line. */ class Bundle { @@ -50,12 +59,12 @@ public: : mCmd(kCommandUnknown), mVerbose(false), mAndroidList(false), mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false), mUpdate(false), mExtending(false), - mRequireLocalization(false), mPseudolocalize(false), + mRequireLocalization(false), mPseudolocalize(NO_PSEUDOLOCALIZATION), mWantUTF16(false), mValues(false), mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL), mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL), mAutoAddOverlay(false), mGenDependencies(false), - mAssetSourceDir(NULL), + mAssetSourceDir(NULL), mCrunchedOutputDir(NULL), mProguardFile(NULL), mAndroidManifestFile(NULL), mPublicOutputFile(NULL), mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL), @@ -94,8 +103,8 @@ public: void setExtending(bool val) { mExtending = val; } bool getRequireLocalization(void) const { return mRequireLocalization; } void setRequireLocalization(bool val) { mRequireLocalization = val; } - bool getPseudolocalize(void) const { return mPseudolocalize; } - void setPseudolocalize(bool val) { mPseudolocalize = val; } + short getPseudolocalize(void) const { return mPseudolocalize; } + void setPseudolocalize(short val) { mPseudolocalize = val; } void setWantUTF16(bool val) { mWantUTF16 = val; } bool getValues(void) const { return mValues; } void setValues(bool val) { mValues = val; } @@ -250,7 +259,7 @@ private: bool mUpdate; bool mExtending; bool mRequireLocalization; - bool mPseudolocalize; + short mPseudolocalize; bool mWantUTF16; bool mValues; int mCompressionMethod; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index c7cce96..f7de558 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -1885,14 +1885,17 @@ int doPackage(Bundle* bundle) FILE* fp; String8 dependencyFile; - // -c zz_ZZ means do pseudolocalization + // -c en_XA or/and ar_XB means do pseudolocalization ResourceFilter filter; err = filter.parse(bundle->getConfigurations()); if (err != NO_ERROR) { goto bail; } if (filter.containsPseudo()) { - bundle->setPseudolocalize(true); + bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED); + } + if (filter.containsPseudoBidi()) { + bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI); } N = bundle->getFileSpecCount(); diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp index e8a2be4..8ca852e 100644 --- a/tools/aapt/ResourceFilter.cpp +++ b/tools/aapt/ResourceFilter.cpp @@ -24,8 +24,10 @@ ResourceFilter::parse(const char* arg) String8 part(p, q-p); - if (part == "zz_ZZ") { - mContainsPseudo = true; + if (part == "en_XA") { + mContainsPseudoAccented = true; + } else if (part == "ar_XB") { + mContainsPseudoBidi = true; } int axis; AxisValue value; diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h index 0d127ba..c57770e 100644 --- a/tools/aapt/ResourceFilter.h +++ b/tools/aapt/ResourceFilter.h @@ -16,19 +16,22 @@ class ResourceFilter { public: - ResourceFilter() : mData(), mContainsPseudo(false) {} + ResourceFilter() : mData(), mContainsPseudoAccented(false), + mContainsPseudoBidi(false) {} status_t parse(const char* arg); bool isEmpty() const; bool match(int axis, const ResTable_config& config) const; bool match(const ResTable_config& config) const; const SortedVector<AxisValue>* configsForAxis(int axis) const; - inline bool containsPseudo() const { return mContainsPseudo; } + inline bool containsPseudo() const { return mContainsPseudoAccented; } + inline bool containsPseudoBidi() const { return mContainsPseudoBidi; } private: bool match(int axis, const AxisValue& value) const; KeyedVector<int,SortedVector<AxisValue> > mData; - bool mContainsPseudo; + bool mContainsPseudoAccented; + bool mContainsPseudoBidi; }; #endif diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 0b1f985..cf271a9 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -25,7 +25,7 @@ status_t compileXmlFile(const sp<AaptAssets>& assets, if (root == NULL) { return UNKNOWN_ERROR; } - + return compileXmlFile(assets, root, target, table, options); } @@ -577,13 +577,13 @@ status_t parseAndAddBag(Bundle* bundle, int32_t curFormat, bool isFormatted, const String16& product, - bool pseudolocalize, + PseudolocalizationMethod pseudolocalize, const bool overwrite, ResourceTable* outTable) { status_t err; const String16 item16("item"); - + String16 str; Vector<StringPool::entry_style_span> spans; err = parseStyledString(bundle, in->getPrintableSource().string(), @@ -672,7 +672,7 @@ status_t parseAndAddEntry(Bundle* bundle, int32_t curFormat, bool isFormatted, const String16& product, - bool pseudolocalize, + PseudolocalizationMethod pseudolocalize, const bool overwrite, KeyedVector<type_ident_pair_t, bool>* skippedResourceNames, ResourceTable* outTable) @@ -854,10 +854,31 @@ status_t compileResourceFile(Bundle* bundle, ResTable_config curParams(defParams); ResTable_config pseudoParams(curParams); - pseudoParams.language[0] = 'z'; - pseudoParams.language[1] = 'z'; - pseudoParams.country[0] = 'Z'; - pseudoParams.country[1] = 'Z'; + pseudoParams.language[0] = 'e'; + pseudoParams.language[1] = 'n'; + pseudoParams.country[0] = 'X'; + pseudoParams.country[1] = 'A'; + + ResTable_config pseudoBidiParams(curParams); + pseudoBidiParams.language[0] = 'a'; + pseudoBidiParams.language[1] = 'r'; + pseudoBidiParams.country[0] = 'X'; + pseudoBidiParams.country[1] = 'B'; + + // We should skip resources for pseudolocales if they were + // already added automatically. This is a fix for a transition period when + // manually pseudolocalized resources may be expected. + // TODO: remove this check after next SDK version release. + if ((bundle->getPseudolocalize() & PSEUDO_ACCENTED && + curParams.locale == pseudoParams.locale) || + (bundle->getPseudolocalize() & PSEUDO_BIDI && + curParams.locale == pseudoBidiParams.locale)) { + SourcePos(in->getPrintableSource(), 0).warning( + "Resource file %s is skipped as pseudolocalization" + " was done automatically.", + in->getPrintableSource().string()); + return NO_ERROR; + } while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::START_TAG) { @@ -1345,7 +1366,7 @@ status_t compileResourceFile(Bundle* bundle, curType = string16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; curIsStyled = true; - curIsPseudolocalizable = (translatable != false16); + curIsPseudolocalizable = fileIsTranslatable && (translatable != false16); } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) { curTag = &drawable16; curType = drawable16; @@ -1389,6 +1410,7 @@ status_t compileResourceFile(Bundle* bundle, curTag = &plurals16; curType = plurals16; curIsBag = true; + curIsPseudolocalizable = fileIsTranslatable; } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) { curTag = &array16; curType = array16; @@ -1410,25 +1432,23 @@ status_t compileResourceFile(Bundle* bundle, } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) { // Check whether these strings need valid formats. // (simplified form of what string16 does above) + bool isTranslatable = false; size_t n = block.getAttributeCount(); // Pseudolocalizable by default, unless this string array isn't // translatable. - curIsPseudolocalizable = true; for (size_t i = 0; i < n; i++) { size_t length; const uint16_t* attr = block.getAttributeName(i, &length); - if (strcmp16(attr, translatable16.string()) == 0) { + if (strcmp16(attr, formatted16.string()) == 0) { const uint16_t* value = block.getAttributeStringValue(i, &length); if (strcmp16(value, false16.string()) == 0) { - curIsPseudolocalizable = false; + curIsFormatted = false; } - } - - if (strcmp16(attr, formatted16.string()) == 0) { + } else if (strcmp16(attr, translatable16.string()) == 0) { const uint16_t* value = block.getAttributeStringValue(i, &length); if (strcmp16(value, false16.string()) == 0) { - curIsFormatted = false; + isTranslatable = false; } } } @@ -1438,6 +1458,7 @@ status_t compileResourceFile(Bundle* bundle, curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; curIsBag = true; curIsBagReplaceOnOverwrite = true; + curIsPseudolocalizable = isTranslatable && fileIsTranslatable; } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) { curTag = &integer_array16; curType = array16; @@ -1559,19 +1580,29 @@ status_t compileResourceFile(Bundle* bundle, err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType, ident, parentIdent, itemIdent, curFormat, curIsFormatted, - product, false, overwrite, outTable); + product, NO_PSEUDOLOCALIZATION, overwrite, outTable); if (err == NO_ERROR) { if (curIsPseudolocalizable && localeIsDefined(curParams) - && bundle->getPseudolocalize()) { + && bundle->getPseudolocalize() > 0) { // pseudolocalize here -#if 1 - block.setPosition(parserPosition); - err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage, - curType, ident, parentIdent, itemIdent, curFormat, - curIsFormatted, product, true, overwrite, outTable); -#endif + if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) == + PSEUDO_ACCENTED) { + block.setPosition(parserPosition); + err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage, + curType, ident, parentIdent, itemIdent, curFormat, + curIsFormatted, product, PSEUDO_ACCENTED, + overwrite, outTable); + } + if ((PSEUDO_BIDI & bundle->getPseudolocalize()) == + PSEUDO_BIDI) { + block.setPosition(parserPosition); + err = parseAndAddBag(bundle, in, &block, pseudoBidiParams, myPackage, + curType, ident, parentIdent, itemIdent, curFormat, + curIsFormatted, product, PSEUDO_BIDI, + overwrite, outTable); + } } - } + } if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -1592,20 +1623,31 @@ status_t compileResourceFile(Bundle* bundle, err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident, *curTag, curIsStyled, curFormat, curIsFormatted, - product, false, overwrite, &skippedResourceNames, outTable); + product, NO_PSEUDOLOCALIZATION, overwrite, &skippedResourceNames, outTable); if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR? hasErrors = localHasErrors = true; } else if (err == NO_ERROR) { if (curIsPseudolocalizable && localeIsDefined(curParams) - && bundle->getPseudolocalize()) { + && bundle->getPseudolocalize() > 0) { // pseudolocalize here - block.setPosition(parserPosition); - err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType, - ident, *curTag, curIsStyled, curFormat, - curIsFormatted, product, - true, overwrite, &skippedResourceNames, outTable); + if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) == + PSEUDO_ACCENTED) { + block.setPosition(parserPosition); + err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType, + ident, *curTag, curIsStyled, curFormat, + curIsFormatted, product, + PSEUDO_ACCENTED, overwrite, &skippedResourceNames, outTable); + } + if ((PSEUDO_BIDI & bundle->getPseudolocalize()) == + PSEUDO_BIDI) { + block.setPosition(parserPosition); + err = parseAndAddEntry(bundle, in, &block, pseudoBidiParams, + myPackage, curType, ident, *curTag, curIsStyled, curFormat, + curIsFormatted, product, + PSEUDO_BIDI, overwrite, &skippedResourceNames, outTable); + } if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -2636,8 +2678,8 @@ ResourceTable::validateLocalizations(void) continue; } - // don't bother with the pseudolocale "zz_ZZ" - if (config != "zz_ZZ") { + // don't bother with the pseudolocale "en_XA" or "ar_XB" + if (config != "en_XA" && config != "ar_XB") { if (configSrcMap.find(config) == configSrcMap.end()) { // okay, no specific localization found. it's possible that we are // requiring a specific regional localization [e.g. de_DE] but there is an diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp index a663ad5..607d419 100644 --- a/tools/aapt/XMLNode.cpp +++ b/tools/aapt/XMLNode.cpp @@ -187,7 +187,7 @@ status_t parseStyledString(Bundle* bundle, String16* outString, Vector<StringPool::entry_style_span>* outSpans, bool isFormatted, - bool pseudolocalize) + PseudolocalizationMethod pseudolocalize) { Vector<StringPool::entry_style_span> spanStack; String16 curString; @@ -198,21 +198,30 @@ status_t parseStyledString(Bundle* bundle, size_t len; ResXMLTree::event_code_t code; + // Bracketing if pseudolocalization accented method specified. + if (pseudolocalize == PSEUDO_ACCENTED) { + curString.append(String16(String8("["))); + } while ((code=inXml->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { - if (code == ResXMLTree::TEXT) { String16 text(inXml->getText(&len)); if (firstTime && text.size() > 0) { firstTime = false; if (text.string()[0] == '@') { // If this is a resource reference, don't do the pseudoloc. - pseudolocalize = false; + pseudolocalize = NO_PSEUDOLOCALIZATION; } } - if (xliffDepth == 0 && pseudolocalize) { - std::string orig(String8(text).string()); - std::string pseudo = pseudolocalize_string(orig); - curString.append(String16(String8(pseudo.c_str()))); + if (xliffDepth == 0 && pseudolocalize > 0) { + String16 pseudo; + if (pseudolocalize == PSEUDO_ACCENTED) { + pseudo = pseudolocalize_string(text); + } else if (pseudolocalize == PSEUDO_BIDI) { + pseudo = pseudobidi_string(text); + } else { + pseudo = text; + } + curString.append(pseudo); } else { if (isFormatted && hasSubstitutionErrors(fileName, inXml, text) != NO_ERROR) { return UNKNOWN_ERROR; @@ -352,6 +361,25 @@ moveon: } } + // Bracketing if pseudolocalization accented method specified. + if (pseudolocalize == PSEUDO_ACCENTED) { + const char16_t* str = outString->string(); + const char16_t* p = str; + const char16_t* e = p + outString->size(); + int words_cnt = 0; + while (p < e) { + if (isspace(*p)) { + words_cnt++; + } + p++; + } + unsigned int length = words_cnt > 3 ? outString->size() : + outString->size() / 2; + curString.append(String16(String8(" "))); + curString.append(pseudo_generate_expansion(length)); + curString.append(String16(String8("]"))); + } + if (code == ResXMLTree::BAD_DOCUMENT) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Error parsing XML\n"); diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h index 05624b7..ccbf9f4 100644 --- a/tools/aapt/XMLNode.h +++ b/tools/aapt/XMLNode.h @@ -26,7 +26,7 @@ status_t parseStyledString(Bundle* bundle, String16* outString, Vector<StringPool::entry_style_span>* outSpans, bool isFormatted, - bool isPseudolocalizable); + PseudolocalizationMethod isPseudolocalizable); void printXMLBlock(ResXMLTree* block); diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp index 9e50c5a..60aa2b2 100644 --- a/tools/aapt/pseudolocalize.cpp +++ b/tools/aapt/pseudolocalize.cpp @@ -2,89 +2,155 @@ using namespace std; +// String basis to generate expansion +static const String16 k_expansion_string = String16("one two three " + "four five six seven eight nine ten eleven twelve thirteen " + "fourteen fiveteen sixteen seventeen nineteen twenty"); + +// Special unicode characters to override directionality of the words +static const String16 k_rlm = String16("\xe2\x80\x8f"); +static const String16 k_rlo = String16("\xE2\x80\xae"); +static const String16 k_pdf = String16("\xE2\x80\xac"); + +// Placeholder marks +static const String16 k_placeholder_open = String16("\xc2\xbb"); +static const String16 k_placeholder_close = String16("\xc2\xab"); + static const char* -pseudolocalize_char(char c) +pseudolocalize_char(const char16_t c) { switch (c) { - case 'a': return "\xc4\x83"; - case 'b': return "\xcf\x84"; - case 'c': return "\xc4\x8b"; - case 'd': return "\xc4\x8f"; - case 'e': return "\xc4\x99"; + case 'a': return "\xc3\xa5"; + case 'b': return "\xc9\x93"; + case 'c': return "\xc3\xa7"; + case 'd': return "\xc3\xb0"; + case 'e': return "\xc3\xa9"; case 'f': return "\xc6\x92"; case 'g': return "\xc4\x9d"; - case 'h': return "\xd1\x9b"; - case 'i': return "\xcf\x8a"; + case 'h': return "\xc4\xa5"; + case 'i': return "\xc3\xae"; case 'j': return "\xc4\xb5"; - case 'k': return "\xc4\xb8"; - case 'l': return "\xc4\xba"; + case 'k': return "\xc4\xb7"; + case 'l': return "\xc4\xbc"; case 'm': return "\xe1\xb8\xbf"; - case 'n': return "\xd0\xb8"; - case 'o': return "\xcf\x8c"; - case 'p': return "\xcf\x81"; + case 'n': return "\xc3\xb1"; + case 'o': return "\xc3\xb6"; + case 'p': return "\xc3\xbe"; case 'q': return "\x51"; - case 'r': return "\xd2\x91"; + case 'r': return "\xc5\x95"; case 's': return "\xc5\xa1"; - case 't': return "\xd1\x82"; - case 'u': return "\xce\xb0"; + case 't': return "\xc5\xa3"; + case 'u': return "\xc3\xbb"; case 'v': return "\x56"; - case 'w': return "\xe1\xba\x85"; + case 'w': return "\xc5\xb5"; case 'x': return "\xd1\x85"; - case 'y': return "\xe1\xbb\xb3"; - case 'z': return "\xc5\xba"; + case 'y': return "\xc3\xbd"; + case 'z': return "\xc5\xbe"; case 'A': return "\xc3\x85"; case 'B': return "\xce\xb2"; - case 'C': return "\xc4\x88"; - case 'D': return "\xc4\x90"; - case 'E': return "\xd0\x84"; - case 'F': return "\xce\x93"; - case 'G': return "\xc4\x9e"; - case 'H': return "\xc4\xa6"; - case 'I': return "\xd0\x87"; - case 'J': return "\xc4\xb5"; + case 'C': return "\xc3\x87"; + case 'D': return "\xc3\x90"; + case 'E': return "\xc3\x89"; + case 'G': return "\xc4\x9c"; + case 'H': return "\xc4\xa4"; + case 'I': return "\xc3\x8e"; + case 'J': return "\xc4\xb4"; case 'K': return "\xc4\xb6"; - case 'L': return "\xc5\x81"; + case 'L': return "\xc4\xbb"; case 'M': return "\xe1\xb8\xbe"; - case 'N': return "\xc5\x83"; - case 'O': return "\xce\x98"; - case 'P': return "\xcf\x81"; + case 'N': return "\xc3\x91"; + case 'O': return "\xc3\x96"; + case 'P': return "\xc3\x9e"; case 'Q': return "\x71"; - case 'R': return "\xd0\xaf"; - case 'S': return "\xc8\x98"; - case 'T': return "\xc5\xa6"; - case 'U': return "\xc5\xa8"; + case 'R': return "\xc5\x94"; + case 'S': return "\xc5\xa0"; + case 'T': return "\xc5\xa2"; + case 'U': return "\xc3\x9b"; case 'V': return "\xce\xbd"; - case 'W': return "\xe1\xba\x84"; + case 'W': return "\xc5\xb4"; case 'X': return "\xc3\x97"; - case 'Y': return "\xc2\xa5"; + case 'Y': return "\xc3\x9d"; case 'Z': return "\xc5\xbd"; + case '!': return "\xc2\xa1"; + case '?': return "\xc2\xbf"; + case '$': return "\xe2\x82\xac"; default: return NULL; } } +static bool +is_possible_normal_placeholder_end(const char16_t c) { + switch (c) { + case 's': return true; + case 'S': return true; + case 'c': return true; + case 'C': return true; + case 'd': return true; + case 'o': return true; + case 'x': return true; + case 'X': return true; + case 'f': return true; + case 'e': return true; + case 'E': return true; + case 'g': return true; + case 'G': return true; + case 'a': return true; + case 'A': return true; + case 'b': return true; + case 'B': return true; + case 'h': return true; + case 'H': return true; + case '%': return true; + case 'n': return true; + default: return false; + } +} + +String16 +pseudo_generate_expansion(const unsigned int length) { + String16 result = k_expansion_string; + const char16_t* s = result.string(); + if (result.size() < length) { + result += String16(" "); + result += pseudo_generate_expansion(length - result.size()); + } else { + int ext = 0; + // Should contain only whole words, so looking for a space + for (unsigned int i = length + 1; i < result.size(); ++i) { + ++ext; + if (s[i] == ' ') { + break; + } + } + result.remove(length + ext, 0); + } + return result; +} + /** * Converts characters so they look like they've been localized. * * Note: This leaves escape sequences untouched so they can later be * processed by ResTable::collectString in the normal way. */ -string -pseudolocalize_string(const string& source) +String16 +pseudolocalize_string(const String16& source) { - const char* s = source.c_str(); - string result; - const size_t I = source.length(); + const char16_t* s = source.string(); + String16 result; + const size_t I = source.size(); for (size_t i=0; i<I; i++) { - char c = s[i]; + char16_t c = s[i]; if (c == '\\') { + // Escape syntax, no need to pseudolocalize if (i<I-1) { - result += '\\'; + result += String16("\\"); i++; c = s[i]; switch (c) { case 'u': // this one takes up 5 chars - result += string(s+i, 5); + result += String16(s+i, 5); i += 4; break; case 't': @@ -96,24 +162,107 @@ pseudolocalize_string(const string& source) case '\'': case '\\': default: - result += c; + result.append(&c, 1); break; } } else { - result += c; + result.append(&c, 1); + } + } else if (c == '%') { + // Placeholder syntax, no need to pseudolocalize + result += k_placeholder_open; + bool end = false; + result.append(&c, 1); + while (!end && i < I) { + ++i; + c = s[i]; + result.append(&c, 1); + if (is_possible_normal_placeholder_end(c)) { + end = true; + } else if (c == 't') { + ++i; + c = s[i]; + result.append(&c, 1); + end = true; + } + } + result += k_placeholder_close; + } else if (c == '<' || c == '&') { + // html syntax, no need to pseudolocalize + bool tag_closed = false; + while (!tag_closed && i < I) { + if (c == '&') { + String16 escape_text; + escape_text.append(&c, 1); + bool end = false; + size_t htmlCodePos = i; + while (!end && htmlCodePos < I) { + ++htmlCodePos; + c = s[htmlCodePos]; + escape_text.append(&c, 1); + // Valid html code + if (c == ';') { + end = true; + i = htmlCodePos; + } + // Wrong html code + else if (!((c == '#' || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9')))) { + end = true; + } + } + result += escape_text; + if (escape_text != String16("<")) { + tag_closed = true; + } + continue; + } + if (c == '>') { + tag_closed = true; + result.append(&c, 1); + continue; + } + result.append(&c, 1); + i++; + c = s[i]; } } else { + // This is a pure text that should be pseudolocalized const char* p = pseudolocalize_char(c); if (p != NULL) { - result += p; + result += String16(p); } else { - result += c; + result.append(&c, 1); } } } - - //printf("result=\'%s\'\n", result.c_str()); return result; } +String16 +pseudobidi_string(const String16& source) +{ + const char16_t* s = source.string(); + String16 result; + result += k_rlm; + result += k_rlo; + for (size_t i=0; i<source.size(); i++) { + char16_t c = s[i]; + switch(c) { + case ' ': result += k_pdf; + result += k_rlm; + result.append(&c, 1); + result += k_rlm; + result += k_rlo; + break; + default: result.append(&c, 1); + break; + } + } + result += k_pdf; + result += k_rlm; + return result; +} diff --git a/tools/aapt/pseudolocalize.h b/tools/aapt/pseudolocalize.h index 94cb034..e6ab18e 100644 --- a/tools/aapt/pseudolocalize.h +++ b/tools/aapt/pseudolocalize.h @@ -1,9 +1,18 @@ #ifndef HOST_PSEUDOLOCALIZE_H #define HOST_PSEUDOLOCALIZE_H +#include "StringPool.h" + #include <string> -std::string pseudolocalize_string(const std::string& source); +String16 pseudolocalize_string(const String16& source); +// Surrounds every word in the sentance with specific characters that makes +// the word directionality RTL. +String16 pseudobidi_string(const String16& source); +// Generates expansion string based on the specified lenght. +// Generated string could not be shorter that length, but it could be slightly +// longer. +String16 pseudo_generate_expansion(const unsigned int length); #endif // HOST_PSEUDOLOCALIZE_H diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java index 998b08b..0f66fd7 100644 --- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java +++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java @@ -247,4 +247,14 @@ public class ICU_Delegate { return true; } + + @LayoutlibDelegate + /*package*/ static void setDefaultLocale(String locale) { + ICU.setDefaultLocale(locale); + } + + @LayoutlibDelegate + /*package*/ static String getDefaultLocale() { + return ICU.getDefaultLocale(); + } } |
