diff options
55 files changed, 2138 insertions, 89 deletions
diff --git a/api/current.txt b/api/current.txt index 4a23046..308e4d4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -20254,6 +20254,7 @@ package android.telephony.cdma { public class CdmaCellLocation extends android.telephony.CellLocation { ctor public CdmaCellLocation(); ctor public CdmaCellLocation(android.os.Bundle); + method public static double convertQuartSecToDecDegrees(int); method public void fillInNotifierBundle(android.os.Bundle); method public int getBaseStationId(); method public int getBaseStationLatitude(); diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index f277339..3e722ea 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -34,6 +34,12 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libdiskusage +ifeq ($(HAVE_SELINUX),true) +LOCAL_C_INCLUDES += external/libselinux/include +LOCAL_SHARED_LIBRARIES += libselinux +LOCAL_CFLAGS := -DHAVE_SELINUX +endif # HAVE_SELINUX + LOCAL_MODULE := installd LOCAL_MODULE_TAGS := optional diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index 96a6438..1bb4935 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -17,6 +17,10 @@ #include "installd.h" #include <diskusage/dirsize.h> +#ifdef HAVE_SELINUX +#include <selinux/android.h> +#endif + /* Directory records that are used in execution of commands. */ dir_rec_t android_data_dir; dir_rec_t android_asec_dir; @@ -72,12 +76,31 @@ int install(const char *pkgname, uid_t uid, gid_t gid) return -errno; } +#ifdef HAVE_SELINUX + if (selinux_android_setfilecon(libdir, pkgname, AID_SYSTEM) < 0) { + LOGE("cannot setfilecon dir '%s': %s\n", libdir, strerror(errno)); + unlink(libdir); + unlink(pkgdir); + return -errno; + } +#endif + if (chown(pkgdir, uid, gid) < 0) { ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno)); unlink(libdir); unlink(pkgdir); return -errno; } + +#ifdef HAVE_SELINUX + if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) { + LOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(libdir); + unlink(pkgdir); + return -errno; + } +#endif + return 0; } @@ -175,6 +198,15 @@ int make_user_data(const char *pkgname, uid_t uid, uid_t persona) unlink(pkgdir); return -errno; } + +#ifdef HAVE_SELINUX + if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) { + LOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(pkgdir); + return -errno; + } +#endif + return 0; } @@ -366,12 +398,18 @@ int protect(char *pkgname, gid_t gid) ALOGE("failed to chgrp '%s': %s\n", pkgpath, strerror(errno)); return -1; } - if (chmod(pkgpath, S_IRUSR|S_IWUSR|S_IRGRP) < 0) { ALOGE("failed to chmod '%s': %s\n", pkgpath, strerror(errno)); return -1; } +#ifdef HAVE_SELINUX + if (selinux_android_setfilecon(pkgpath, pkgname, s.st_uid) < 0) { + LOGE("cannot setfilecon dir '%s': %s\n", pkgpath, strerror(errno)); + return -1; + } +#endif + return 0; } diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index dd9ea26..3a67cec 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -64,11 +64,12 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } /** - * Called when the application is starting, before any other application - * objects have been created. Implementations should be as quick as - * possible (for example using lazy initialization of state) since the time - * spent in this function directly impacts the performance of starting the - * first activity, service, or receiver in a process. + * Called when the application is starting, before any activity, service, + * or receiver objects (excluding content providers) have been created. + * Implementations should be as quick as possible (for example using + * lazy initialization of state) since the time spent in this function + * directly impacts the performance of starting the first activity, + * service, or receiver in a process. * If you override this method, be sure to call super.onCreate(). */ public void onCreate() { diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 3e726e0..8e6278d 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -385,6 +385,7 @@ public abstract class ApplicationThreadNative extends Binder case SCHEDULE_LOW_MEMORY_TRANSACTION: { + data.enforceInterface(IApplicationThread.descriptor); scheduleLowMemory(); return true; } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 4ed0766..0b58396 100644..100755 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1008,13 +1008,15 @@ public class DevicePolicyManager { /** * Ask the user date be wiped. This will cause the device to reboot, * erasing all user data while next booting up. External storage such - * as SD cards will not be erased. + * as SD cards will be also erased if the flag {@link #WIPE_EXTERNAL_STORAGE} + * is set. * * <p>The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to be able to call * this method; if it has not, a security exception will be thrown. * - * @param flags Bit mask of additional options: currently must be 0. + * @param flags Bit mask of additional options: currently 0 and + * {@link #WIPE_EXTERNAL_STORAGE} are supported. */ public void wipeData(int flags) { if (mService != null) { diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java new file mode 100644 index 0000000..90cfa37 --- /dev/null +++ b/core/java/android/os/SELinux.java @@ -0,0 +1,105 @@ +package android.os; + +import java.io.FileDescriptor; + +/** + * This class provides access to the centralized jni bindings for + * SELinux interaction. + * {@hide} + */ +public class SELinux { + + /** + * Determine whether SELinux is disabled or enabled. + * @return a boolean indicating whether SELinux is enabled. + */ + public static final native boolean isSELinuxEnabled(); + + /** + * Determine whether SELinux is permissive or enforcing. + * @return a boolean indicating whether SELinux is enforcing. + */ + public static final native boolean isSELinuxEnforced(); + + /** + * Set whether SELinux is permissive or enforcing. + * @param boolean representing whether to set SELinux to enforcing + * @return a boolean representing whether the desired mode was set + */ + public static final native boolean setSELinuxEnforce(boolean value); + + /** + * Sets the security context for newly created file objects. + * @param context a security context given as a String. + * @return a boolean indicating whether the operation succeeded. + */ + public static final native boolean setFSCreateContext(String context); + + /** + * Change the security context of an existing file object. + * @param path representing the path of file object to relabel. + * @param con new security context given as a String. + * @return a boolean indicating whether the operation succeeded. + */ + public static final native boolean setFileContext(String path, String context); + + /** + * Get the security context of a file object. + * @param path the pathname of the file object. + * @return a security context given as a String. + */ + public static final native String getFileContext(String path); + + /** + * Get the security context of a peer socket. + * @param fd FileDescriptor class of the peer socket. + * @return a String representing the peer socket security context. + */ + public static final native String getPeerContext(FileDescriptor fd); + + /** + * Gets the security context of the current process. + * @return a String representing the security context of the current process. + */ + public static final native String getContext(); + + /** + * Gets the security context of a given process id. + * Use of this function is discouraged for Binder transactions. + * Use Binder.getCallingSecctx() instead. + * @param pid an int representing the process id to check. + * @return a String representing the security context of the given pid. + */ + public static final native String getPidContext(int pid); + + /** + * Gets a list of the SELinux boolean names. + * @return an array of strings containing the SELinux boolean names. + */ + public static final native String[] getBooleanNames(); + + /** + * Gets the value for the given SELinux boolean name. + * @param String The name of the SELinux boolean. + * @return a boolean indicating whether the SELinux boolean is set. + */ + public static final native boolean getBooleanValue(String name); + + /** + * Sets the value for the given SELinux boolean name. + * @param String The name of the SELinux boolean. + * @param Boolean The new value of the SELinux boolean. + * @return a boolean indicating whether or not the operation succeeded. + */ + public static final native boolean setBooleanValue(String name, boolean value); + + /** + * Check permissions between two security contexts. + * @param scon The source or subject security context. + * @param tcon The target or object security context. + * @param tclass The object security class name. + * @param perm The permission name. + * @return a boolean indicating whether permission was granted. + */ + public static final native boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm); +} diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index e4e7239..72e429c 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -1195,15 +1195,15 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList case KeyEvent.KEYCODE_DPAD_LEFT: if (movePrevious()) { playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); + return true; } - return true; - + break; case KeyEvent.KEYCODE_DPAD_RIGHT: if (moveNext()) { playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); + return true; } - return true; - + break; case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: mReceivedInvokeKeyDown = true; diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 8975109..ada7dd1 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -2207,8 +2207,13 @@ public class GridView extends AbsListView { int height = view.getHeight(); if (height > 0) { final int numColumns = mNumColumns; - final int whichRow = mFirstPosition / numColumns; final int rowCount = (mItemCount + numColumns - 1) / numColumns; + // In case of stackFromBottom the calculation of whichRow needs + // to take into account that counting from the top the first row + // might not be entirely filled. + final int oddItemsOnFirstRow = isStackFromBottom() ? ((rowCount * numColumns) - + mItemCount) : 0; + final int whichRow = (mFirstPosition + oddItemsOnFirstRow) / numColumns; return Math.max(whichRow * 100 - (top * 100) / height + (int) ((float) mScrollY / getHeight() * rowCount * 100), 0); } diff --git a/core/java/android/widget/SimpleExpandableListAdapter.java b/core/java/android/widget/SimpleExpandableListAdapter.java index 015c169..f514374 100644 --- a/core/java/android/widget/SimpleExpandableListAdapter.java +++ b/core/java/android/widget/SimpleExpandableListAdapter.java @@ -38,6 +38,8 @@ import java.util.Map; */ public class SimpleExpandableListAdapter extends BaseExpandableListAdapter { private List<? extends Map<String, ?>> mGroupData; + // Keeps track of if a group is currently expanded or not + private boolean[] mIsGroupExpanded; private int mExpandedGroupLayout; private int mCollapsedGroupLayout; private String[] mGroupFrom; @@ -196,6 +198,8 @@ public class SimpleExpandableListAdapter extends BaseExpandableListAdapter { int childLayout, int lastChildLayout, String[] childFrom, int[] childTo) { mGroupData = groupData; + // Initially all groups are not expanded + mIsGroupExpanded = new boolean[groupData.size()]; mExpandedGroupLayout = expandedGroupLayout; mCollapsedGroupLayout = collapsedGroupLayout; mGroupFrom = groupFrom; @@ -298,4 +302,52 @@ public class SimpleExpandableListAdapter extends BaseExpandableListAdapter { return true; } + /** + * {@inheritDoc} + * @return 1 for the last child in a group, 0 for the other children. + */ + @Override + public int getChildType(int groupPosition, int childPosition) { + final int childrenInGroup = getChildrenCount(groupPosition); + return childPosition == childrenInGroup - 1 ? 1 : 0; + } + + /** + * {@inheritDoc} + * @return 2, one type for the last child in a group, one for the other children. + */ + @Override + public int getChildTypeCount() { + return 2; + } + + /** + * {@inheritDoc} + * @return 1 for an expanded group view, 0 for a collapsed one. + */ + @Override + public int getGroupType(int groupPosition) { + return mIsGroupExpanded[groupPosition] ? 1 : 0; + } + + /** + * {@inheritDoc} + * @return 2, one for a collapsed group view, one for an expanded one. + */ + @Override + public int getGroupTypeCount() { + return 2; + } + + /** {@inheritDoc} */ + @Override + public void onGroupCollapsed(int groupPosition) { + mIsGroupExpanded[groupPosition] = false; + } + + /** {@inheritDoc} */ + @Override + public void onGroupExpanded(int groupPosition) { + mIsGroupExpanded[groupPosition] = true; + } } diff --git a/core/java/com/android/internal/os/ProcessStats.java b/core/java/com/android/internal/os/ProcessStats.java index 1923b86..b1bb8c1 100644 --- a/core/java/com/android/internal/os/ProcessStats.java +++ b/core/java/com/android/internal/os/ProcessStats.java @@ -154,7 +154,7 @@ public class ProcessStats { private boolean mFirst = true; - private byte[] mBuffer = new byte[256]; + private byte[] mBuffer = new byte[4096]; /** * The time in microseconds that the CPU has been running at each speed. @@ -556,7 +556,7 @@ public class ProcessStats { private long[] getCpuSpeedTimes(long[] out) { long[] tempTimes = out; long[] tempSpeeds = mCpuSpeeds; - final int MAX_SPEEDS = 20; + final int MAX_SPEEDS = 60; if (out == null) { tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that tempSpeeds = new long[MAX_SPEEDS]; diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 73324c0..cf6029e 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -278,7 +278,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter */ public boolean showOverflowMenu() { if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null && - mPostedOpenRunnable == null) { + mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) { OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true); mPostedOpenRunnable = new OpenOverflowRunnable(popup); // Post this for later; we might still need a layout for the anchor to be right. diff --git a/core/jni/Android.mk b/core/jni/Android.mk index c24f6c6..b5a2f98 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -66,6 +66,7 @@ LOCAL_SRC_FILES:= \ android_os_MessageQueue.cpp \ android_os_ParcelFileDescriptor.cpp \ android_os_Parcel.cpp \ + android_os_SELinux.cpp \ android_os_StatFs.cpp \ android_os_SystemClock.cpp \ android_os_SystemProperties.cpp \ @@ -218,6 +219,12 @@ LOCAL_SHARED_LIBRARIES := \ libharfbuzz \ libz +ifeq ($(HAVE_SELINUX),true) +LOCAL_C_INCLUDES += external/libselinux/include +LOCAL_SHARED_LIBRARIES += libselinux +LOCAL_CFLAGS += -DHAVE_SELINUX +endif # HAVE_SELINUX + ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SHARED_LIBRARIES += libhwui endif diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 241a905..7a23747 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -133,6 +133,7 @@ extern int register_android_os_Debug(JNIEnv* env); extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_Parcel(JNIEnv* env); extern int register_android_os_ParcelFileDescriptor(JNIEnv *env); +extern int register_android_os_SELinux(JNIEnv* env); extern int register_android_os_StatFs(JNIEnv *env); extern int register_android_os_SystemProperties(JNIEnv *env); extern int register_android_os_SystemClock(JNIEnv* env); @@ -1146,6 +1147,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_FileUtils), REG_JNI(register_android_os_MessageQueue), REG_JNI(register_android_os_ParcelFileDescriptor), + REG_JNI(register_android_os_SELinux), REG_JNI(register_android_os_StatFs), REG_JNI(register_android_os_Trace), REG_JNI(register_android_os_UEventObserver), diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp new file mode 100644 index 0000000..eb99d2b --- /dev/null +++ b/core/jni/android_os_SELinux.cpp @@ -0,0 +1,502 @@ +#define LOG_TAG "SELinuxJNI" +#include <utils/Log.h> + +#include "JNIHelp.h" +#include "jni.h" +#include "android_runtime/AndroidRuntime.h" +#ifdef HAVE_SELINUX +#include "selinux/selinux.h" +#endif +#include <errno.h> + +namespace android { + + static jboolean isSELinuxDisabled = true; + + static void throw_NullPointerException(JNIEnv *env, const char* msg) { + jclass clazz; + clazz = env->FindClass("java/lang/NullPointerException"); + env->ThrowNew(clazz, msg); + } + + /* + * Function: isSELinuxEnabled + * Purpose: checks whether SELinux is enabled/disbaled + * Parameters: none + * Return value : true (enabled) or false (disabled) + * Exceptions: none + */ + static jboolean isSELinuxEnabled(JNIEnv *env, jobject classz) { + + return !isSELinuxDisabled; + } + + /* + * Function: isSELinuxEnforced + * Purpose: return the current SELinux enforce mode + * Parameters: none + * Return value: true (enforcing) or false (permissive) + * Exceptions: none + */ + static jboolean isSELinuxEnforced(JNIEnv *env, jobject clazz) { +#ifdef HAVE_SELINUX + return (security_getenforce() == 1) ? true : false; +#else + return false; +#endif + } + + /* + * Function: setSELinuxEnforce + * Purpose: set the SE Linux enforcing mode + * Parameters: true (enforcing) or false (permissive) + * Return value: true (success) or false (fail) + * Exceptions: none + */ + static jboolean setSELinuxEnforce(JNIEnv *env, jobject clazz, jboolean value) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return false; + + int enforce = (value) ? 1 : 0; + + return (security_setenforce(enforce) != -1) ? true : false; +#else + return false; +#endif + } + + /* + * Function: getPeerCon + * Purpose: retrieves security context of peer socket + * Parameters: + * fileDescriptor: peer socket file as a FileDescriptor object + * Returns: jstring representing the security_context of socket or NULL if error + * Exceptions: NullPointerException if fileDescriptor object is NULL + */ + static jstring getPeerCon(JNIEnv *env, jobject clazz, jobject fileDescriptor) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return NULL; + + if (fileDescriptor == NULL) { + throw_NullPointerException(env, "Trying to check security context of a null peer socket."); + return NULL; + } + + security_context_t context = NULL; + jstring securityString = NULL; + + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + LOGE("There was an issue with retrieving the file descriptor"); + goto bail; + } + + if (getpeercon(fd, &context) == -1) + goto bail; + + LOGV("getPeerCon: Successfully retrived context of peer socket '%s'", context); + + securityString = env->NewStringUTF(context); + + bail: + if (context != NULL) + freecon(context); + + return securityString; +#else + return NULL; +#endif + } + + /* + * Function: setFSCreateCon + * Purpose: set security context used for creating a new file system object + * Parameters: + * context: security_context_t representing the new context of a file system object, + * set to NULL to return to the default policy behavior + * Returns: true on success, false on error + * Exception: none + */ + static jboolean setFSCreateCon(JNIEnv *env, jobject clazz, jstring context) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return false; + + char * securityContext = NULL; + const char *constant_securityContext = NULL; + + if (context != NULL) { + constant_securityContext = env->GetStringUTFChars(context, NULL); + + // GetStringUTFChars returns const char * yet setfscreatecon needs char * + securityContext = const_cast<char *>(constant_securityContext); + } + + int ret; + if ((ret = setfscreatecon(securityContext)) == -1) + goto bail; + + LOGV("setFSCreateCon: set new security context to '%s' ", context == NULL ? "default", context); + + bail: + if (constant_securityContext != NULL) + env->ReleaseStringUTFChars(context, constant_securityContext); + + return (ret == 0) ? true : false; +#else + return false; +#endif + } + + /* + * Function: setFileCon + * Purpose: set the security context of a file object + * Parameters: + * path: the location of the file system object + * con: the new security context of the file system object + * Returns: true on success, false on error + * Exception: NullPointerException is thrown if either path or context strign are NULL + */ + static jboolean setFileCon(JNIEnv *env, jobject clazz, jstring path, jstring con) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return false; + + if (path == NULL) { + throw_NullPointerException(env, "Trying to change the security context of a NULL file object."); + return false; + } + + if (con == NULL) { + throw_NullPointerException(env, "Trying to set the security context of a file object with NULL."); + return false; + } + + const char *objectPath = env->GetStringUTFChars(path, NULL); + const char *constant_con = env->GetStringUTFChars(con, NULL); + + // GetStringUTFChars returns const char * yet setfilecon needs char * + char *newCon = const_cast<char *>(constant_con); + + int ret; + if ((ret = setfilecon(objectPath, newCon)) == -1) + goto bail; + + LOGV("setFileCon: Succesfully set security context '%s' for '%s'", newCon, objectPath); + + bail: + env->ReleaseStringUTFChars(path, objectPath); + env->ReleaseStringUTFChars(con, constant_con); + return (ret == 0) ? true : false; +#else + return false; +#endif + } + + /* + * Function: getFileCon + * Purpose: retrieves the context associated with the given path in the file system + * Parameters: + * path: given path in the file system + * Returns: + * string representing the security context string of the file object + * the string may be NULL if an error occured + * Exceptions: NullPointerException if the path object is null + */ + static jstring getFileCon(JNIEnv *env, jobject clazz, jstring path) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return NULL; + + if (path == NULL) { + throw_NullPointerException(env, "Trying to check security context of a null path."); + return NULL; + } + + const char *objectPath = env->GetStringUTFChars(path, NULL); + + security_context_t context = NULL; + jstring securityString = NULL; + + if (getfilecon(objectPath, &context) == -1) + goto bail; + + LOGV("getFileCon: Successfully retrived context '%s' for file '%s'", context, objectPath); + + securityString = env->NewStringUTF(context); + + bail: + if (context != NULL) + freecon(context); + + env->ReleaseStringUTFChars(path, objectPath); + + return securityString; +#else + return NULL; +#endif + } + + /* + * Function: getCon + * Purpose: Get the context of the current process. + * Parameters: none + * Returns: a jstring representing the security context of the process, + * the jstring may be NULL if there was an error + * Exceptions: none + */ + static jstring getCon(JNIEnv *env, jobject clazz) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return NULL; + + security_context_t context = NULL; + jstring securityString = NULL; + + if (getcon(&context) == -1) + goto bail; + + LOGV("getCon: Successfully retrieved context '%s'", context); + + securityString = env->NewStringUTF(context); + + bail: + if (context != NULL) + freecon(context); + + return securityString; +#else + return NULL; +#endif + } + + /* + * Function: getPidCon + * Purpose: Get the context of a process identified by its pid + * Parameters: + * pid: a jint representing the process + * Returns: a jstring representing the security context of the pid, + * the jstring may be NULL if there was an error + * Exceptions: none + */ + static jstring getPidCon(JNIEnv *env, jobject clazz, jint pid) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return NULL; + + security_context_t context = NULL; + jstring securityString = NULL; + + pid_t checkPid = (pid_t)pid; + + if (getpidcon(checkPid, &context) == -1) + goto bail; + + LOGV("getPidCon: Successfully retrived context '%s' for pid '%d'", context, checkPid); + + securityString = env->NewStringUTF(context); + + bail: + if (context != NULL) + freecon(context); + + return securityString; +#else + return NULL; +#endif + } + + /* + * Function: getBooleanNames + * Purpose: Gets a list of the SELinux boolean names. + * Parameters: None + * Returns: an array of strings containing the SELinux boolean names. + * returns NULL string on error + * Exceptions: None + */ + static jobjectArray getBooleanNames(JNIEnv *env, JNIEnv clazz) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return NULL; + + char **list; + int i, len, ret; + jclass stringClass; + jobjectArray stringArray = NULL; + + if (security_get_boolean_names(&list, &len) == -1) + return NULL; + + stringClass = env->FindClass("java/lang/String"); + stringArray = env->NewObjectArray(len, stringClass, env->NewStringUTF("")); + for (i = 0; i < len; i++) { + jstring obj; + obj = env->NewStringUTF(list[i]); + env->SetObjectArrayElement(stringArray, i, obj); + env->DeleteLocalRef(obj); + free(list[i]); + } + free(list); + + return stringArray; +#else + return NULL; +#endif + } + + /* + * Function: getBooleanValue + * Purpose: Gets the value for the given SELinux boolean name. + * Parameters: + * String: The name of the SELinux boolean. + * Returns: a boolean: (true) boolean is set or (false) it is not. + * Exceptions: None + */ + static jboolean getBooleanValue(JNIEnv *env, jobject clazz, jstring name) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return false; + + const char *boolean_name; + int ret; + + if (name == NULL) + return false; + boolean_name = env->GetStringUTFChars(name, NULL); + ret = security_get_boolean_active(boolean_name); + env->ReleaseStringUTFChars(name, boolean_name); + return (ret == 1) ? true : false; +#else + return false; +#endif + } + + /* + * Function: setBooleanNames + * Purpose: Sets the value for the given SELinux boolean name. + * Parameters: + * String: The name of the SELinux boolean. + * Boolean: The new value of the SELinux boolean. + * Returns: a boolean indicating whether or not the operation succeeded. + * Exceptions: None + */ + static jboolean setBooleanValue(JNIEnv *env, jobject clazz, jstring name, jboolean value) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return false; + + const char *boolean_name = NULL; + int ret; + + if (name == NULL) + return false; + boolean_name = env->GetStringUTFChars(name, NULL); + ret = security_set_boolean(boolean_name, (value) ? 1 : 0); + env->ReleaseStringUTFChars(name, boolean_name); + if (ret) + return false; + + if (security_commit_booleans() == -1) + return false; + + return true; +#else + return false; +#endif + } + + /* + * Function: checkSELinuxAccess + * Purpose: Check permissions between two security contexts. + * Parameters: scon: subject security context as a string + * tcon: object security context as a string + * tclass: object's security class name as a string + * perm: permission name as a string + * Returns: boolean: (true) if permission was granted, (false) otherwise + * Exceptions: None + */ + static jboolean checkSELinuxAccess(JNIEnv *env, jobject clazz, jstring scon, jstring tcon, jstring tclass, jstring perm) { +#ifdef HAVE_SELINUX + if (isSELinuxDisabled) + return true; + + int accessGranted = -1; + + const char *const_scon, *const_tcon, *mytclass, *myperm; + char *myscon, *mytcon; + + if (scon == NULL || tcon == NULL || tclass == NULL || perm == NULL) + goto bail; + + const_scon = env->GetStringUTFChars(scon, NULL); + const_tcon = env->GetStringUTFChars(tcon, NULL); + mytclass = env->GetStringUTFChars(tclass, NULL); + myperm = env->GetStringUTFChars(perm, NULL); + + // selinux_check_access needs char* for some + myscon = const_cast<char *>(const_scon); + mytcon = const_cast<char *>(const_tcon); + + accessGranted = selinux_check_access(myscon, mytcon, mytclass, myperm, NULL); + + LOGV("selinux_check_access returned %d", accessGranted); + + env->ReleaseStringUTFChars(scon, const_scon); + env->ReleaseStringUTFChars(tcon, const_tcon); + env->ReleaseStringUTFChars(tclass, mytclass); + env->ReleaseStringUTFChars(perm, myperm); + + bail: + return (accessGranted == 0) ? true : false; + +#else + return true; +#endif + } + + /* + * JNI registration. + */ + static JNINativeMethod method_table[] = { + + /* name, signature, funcPtr */ + { "checkSELinuxAccess" , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess }, + { "getBooleanNames" , "()[Ljava/lang/String;" , (void*)getBooleanNames }, + { "getBooleanValue" , "(Ljava/lang/String;)Z" , (void*)getBooleanValue }, + { "getContext" , "()Ljava/lang/String;" , (void*)getCon }, + { "getFileContext" , "(Ljava/lang/String;)Ljava/lang/String;" , (void*)getFileCon }, + { "getPeerContext" , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getPeerCon }, + { "getPidContext" , "(I)Ljava/lang/String;" , (void*)getPidCon }, + { "isSELinuxEnforced" , "()Z" , (void*)isSELinuxEnforced}, + { "isSELinuxEnabled" , "()Z" , (void*)isSELinuxEnabled }, + { "setBooleanValue" , "(Ljava/lang/String;Z)Z" , (void*)setBooleanValue }, + { "setFileContext" , "(Ljava/lang/String;Ljava/lang/String;)Z" , (void*)setFileCon }, + { "setFSCreateContext" , "(Ljava/lang/String;)Z" , (void*)setFSCreateCon }, + { "setSELinuxEnforce" , "(Z)Z" , (void*)setSELinuxEnforce}, + }; + + static int log_callback(int type, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + LOG_PRI_VA(ANDROID_LOG_ERROR, "SELinux", fmt, ap); + va_end(ap); + return 0; + } + + int register_android_os_SELinux(JNIEnv *env) { +#ifdef HAVE_SELINUX + union selinux_callback cb; + cb.func_log = log_callback; + selinux_set_callback(SELINUX_CB_LOG, cb); + + isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false; + +#endif + return AndroidRuntime::registerNativeMethods( + env, "android/os/SELinux", + method_table, NELEM(method_table)); + } +} diff --git a/core/res/res/layout/transient_notification.xml b/core/res/res/layout/transient_notification.xml index 21d58aa..5523807 100644 --- a/core/res/res/layout/transient_notification.xml +++ b/core/res/res/layout/transient_notification.xml @@ -29,6 +29,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" + android:layout_gravity="center_horizontal" android:textAppearance="@style/TextAppearance.Small" android:textColor="@color/bright_foreground_dark" android:shadowColor="#BB000000" diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.mk b/core/tests/coretests/apks/install_complete_package_info/Android.mk new file mode 100644 index 0000000..1edccb4 --- /dev/null +++ b/core/tests/coretests/apks/install_complete_package_info/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := FrameworkCoreTests_install_complete_package_info + +include $(BUILD_PACKAGE) + diff --git a/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml b/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml new file mode 100644 index 0000000..4b01736 --- /dev/null +++ b/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.coretests.install_complete_package_info"> + +<!-- + This manifest declares at least one of each of the components that + can be retrieved by PackageManager.getInstalledPackages. + All the implementing classes are empty implementations +--> + + <uses-feature + android:name="com.android.frameworks.coretests.nonexistent" /> + <uses-configuration + android:reqFiveWayNav="false" /> + + <instrumentation + android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.frameworks.coretests" + android:label="Frameworks Core Tests" /> + + <permission + android:label="test permission" + android:name="test_permission" + android:protectionLevel="normal" /> + + <application + android:hasCode="true"> + <activity + android:name="com.android.frameworks.coretests.TestActivity"> + </activity> + <provider + android:name="com.android.frameworks.coretests.TestProvider" + android:authorities="com.android.frameworks.coretests.testprovider" /> + <receiver + android:name="com.android.frameworks.coretests.TestReceiver" /> + <service + android:name="com.android.frameworks.coretests.TestService" /> + </application> +</manifest> diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestActivity.java b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestActivity.java new file mode 100644 index 0000000..10d0551 --- /dev/null +++ b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestActivity.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.frameworks.coretests; + +import android.app.Activity; + +public class TestActivity extends Activity { + +} diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestProvider.java b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestProvider.java new file mode 100644 index 0000000..59f9f10 --- /dev/null +++ b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.frameworks.coretests; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class TestProvider extends ContentProvider { + + @Override + public boolean onCreate() { + return false; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestReceiver.java b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestReceiver.java new file mode 100644 index 0000000..21f6263 --- /dev/null +++ b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestReceiver.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.frameworks.coretests; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class TestReceiver extends ContentProvider { + + @Override + public boolean onCreate() { + return false; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestService.java b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestService.java new file mode 100644 index 0000000..b330e75 --- /dev/null +++ b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestService.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.frameworks.coretests; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class TestService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 77e4986..6e1b9d6 100755 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -23,6 +23,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; @@ -50,6 +51,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.List; + public class PackageManagerTests extends AndroidTestCase { private static final boolean localLOGV = true; public static final String TAG="PackageManagerTests"; @@ -3162,6 +3165,92 @@ public class PackageManagerTests extends AndroidTestCase { assertNotNull("Verifier device identity should not be null", id); } + public void testGetInstalledPackages() { + List<PackageInfo> packages = getPm().getInstalledPackages(0); + assertNotNull("installed packages cannot be null", packages); + assertTrue("installed packages cannot be empty", packages.size() > 0); + } + + public void testGetUnInstalledPackages() { + List<PackageInfo> packages = getPm().getInstalledPackages( + PackageManager.GET_UNINSTALLED_PACKAGES); + assertNotNull("installed packages cannot be null", packages); + assertTrue("installed packages cannot be empty", packages.size() > 0); + } + + /** + * Test that getInstalledPackages returns all the data specified in + * flags. + */ + public void testGetInstalledPackagesAll() { + int flags = PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS + | PackageManager.GET_CONFIGURATIONS | PackageManager.GET_INSTRUMENTATION + | PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS + | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES + | PackageManager.GET_SIGNATURES | PackageManager.GET_UNINSTALLED_PACKAGES; + + List<PackageInfo> packages = getPm().getInstalledPackages(flags); + assertNotNull("installed packages cannot be null", packages); + assertTrue("installed packages cannot be empty", packages.size() > 0); + + PackageInfo packageInfo = null; + + // Find the package with all components specified in the AndroidManifest + // to ensure no null values + for (PackageInfo pi : packages) { + if ("com.android.frameworks.coretests.install_complete_package_info" + .equals(pi.packageName)) { + packageInfo = pi; + break; + } + } + assertNotNull("activities should not be null", packageInfo.activities); + assertNotNull("configPreferences should not be null", packageInfo.configPreferences); + assertNotNull("instrumentation should not be null", packageInfo.instrumentation); + assertNotNull("permissions should not be null", packageInfo.permissions); + assertNotNull("providers should not be null", packageInfo.providers); + assertNotNull("receivers should not be null", packageInfo.receivers); + assertNotNull("services should not be null", packageInfo.services); + assertNotNull("signatures should not be null", packageInfo.signatures); + } + + /** + * Test that getInstalledPackages returns all the data specified in + * flags when the GET_UNINSTALLED_PACKAGES flag is set. + */ + public void testGetUnInstalledPackagesAll() { + int flags = PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS + | PackageManager.GET_CONFIGURATIONS | PackageManager.GET_INSTRUMENTATION + | PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS + | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES + | PackageManager.GET_SIGNATURES | PackageManager.GET_UNINSTALLED_PACKAGES; + + List<PackageInfo> packages = getPm().getInstalledPackages(flags); + assertNotNull("installed packages cannot be null", packages); + assertTrue("installed packages cannot be empty", packages.size() > 0); + + PackageInfo packageInfo = null; + + // Find the package with all components specified in the AndroidManifest + // to ensure no null values + for (PackageInfo pi : packages) { + if ("com.android.frameworks.coretests.install_complete_package_info" + .equals(pi.packageName)) { + packageInfo = pi; + break; + } + } + assertNotNull("activities should not be null", packageInfo.activities); + assertNotNull("configPreferences should not be null", packageInfo.configPreferences); + assertNotNull("instrumentation should not be null", packageInfo.instrumentation); + assertNotNull("permissions should not be null", packageInfo.permissions); + assertNotNull("providers should not be null", packageInfo.providers); + assertNotNull("receivers should not be null", packageInfo.receivers); + assertNotNull("services should not be null", packageInfo.services); + assertNotNull("signatures should not be null", packageInfo.signatures); + } + /*---------- Recommended install location tests ----*/ /* * TODO's diff --git a/core/tests/coretests/src/android/os/SELinuxTest.java b/core/tests/coretests/src/android/os/SELinuxTest.java new file mode 100644 index 0000000..9b63a6b --- /dev/null +++ b/core/tests/coretests/src/android/os/SELinuxTest.java @@ -0,0 +1,45 @@ +package android.os; + +import android.os.Process; +import android.os.SELinux; +import android.test.AndroidTestCase; +import static junit.framework.Assert.assertEquals; + +public class SELinuxTest extends AndroidTestCase { + + public void testgetFileCon() { + if(SELinux.isSELinuxEnabled() == false) + return; + + String ctx = SELinux.getFileContext("/system/bin/toolbox"); + assertEquals(ctx, "u:object_r:system_file:s0"); + } + + public void testgetCon() { + if(SELinux.isSELinuxEnabled() == false) + return; + + String mycon = SELinux.getContext(); + assertEquals(mycon, "u:r:untrusted_app:s0:c33"); + } + + public void testgetPidCon() { + if(SELinux.isSELinuxEnabled() == false) + return; + + String mycon = SELinux.getPidContext(Process.myPid()); + assertEquals(mycon, "u:r:untrusted_app:s0:c33"); + } + + public void testcheckSELinuxAccess() { + if(SELinux.isSELinuxEnabled() == false) + return; + + String mycon = SELinux.getContext(); + boolean ret; + ret = SELinux.checkSELinuxAccess(mycon, mycon, "process", "fork"); + assertEquals(ret,"true"); + ret = SELinux.checkSELinuxAccess(mycon, mycon, "memprotect", "mmap_zero"); + assertEquals(ret,"true"); + } +} diff --git a/core/tests/overlaytests/OverlayTestOverlay/Android.mk b/core/tests/overlaytests/OverlayTestOverlay/Android.mk index cf32c9f..b1327f7 100644 --- a/core/tests/overlaytests/OverlayTestOverlay/Android.mk +++ b/core/tests/overlaytests/OverlayTestOverlay/Android.mk @@ -9,6 +9,4 @@ LOCAL_SDK_VERSION := current LOCAL_PACKAGE_NAME := com.android.overlaytest.overlay -LOCAL_AAPT_FLAGS := -o - include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/OverlayTestOverlay/res/drawable/default_wallpaper.jpg b/core/tests/overlaytests/OverlayTestOverlay/res/drawable-nodpi/default_wallpaper.jpg Binary files differindex 0d944d0..0d944d0 100644 --- a/core/tests/overlaytests/OverlayTestOverlay/res/drawable/default_wallpaper.jpg +++ b/core/tests/overlaytests/OverlayTestOverlay/res/drawable-nodpi/default_wallpaper.jpg diff --git a/core/tests/overlaytests/runtests.sh b/core/tests/overlaytests/runtests.sh index 0ad9efb..0a721ad40 100755 --- a/core/tests/overlaytests/runtests.sh +++ b/core/tests/overlaytests/runtests.sh @@ -18,7 +18,6 @@ function atexit() log=$(mktemp) trap "atexit" EXIT -failures=0 function compile_module() { @@ -38,6 +37,37 @@ function wait_for_boot_completed() $adb wait-for-device logcat | grep -m 1 -e 'PowerManagerService.*bootCompleted' >/dev/null } +function mkdir_if_needed() +{ + local path="$1" + + if [[ "${path:0:1}" != "/" ]]; then + echo "mkdir_if_needed: error: path '$path' does not begin with /" | tee -a $log + exit 1 + fi + + local basename=$(basename "$path") + local dirname=$(dirname "$path") + local t=$($adb shell ls -l $dirname | tr -d '\r' | grep -e "${basename}$" | grep -oe '^.') + + case "$t" in + d) # File exists, and is a directory ... + # do nothing + ;; + l) # ... (or symbolic link possibly to a directory). + # do nothing + ;; + "") # File does not exist. + mkdir_if_needed "$dirname" + $adb shell mkdir "$path" + ;; + *) # File exists, but is not a directory. + echo "mkdir_if_needed: file '$path' exists, but is not a directory" | tee -a $log + exit 1 + ;; + esac +} + function disable_overlay() { echo "Disabling overlay" @@ -48,6 +78,8 @@ function disable_overlay() function enable_overlay() { echo "Enabling overlay" + mkdir_if_needed "/system/vendor" + mkdir_if_needed "/vendor/overlay/framework" $adb shell ln -s /data/app/com.android.overlaytest.overlay.apk /vendor/overlay/framework/framework-res.apk } @@ -59,13 +91,21 @@ function instrument() $adb shell am instrument -w -e class $class com.android.overlaytest/android.test.InstrumentationTestRunner | tee -a $log } +function remount() +{ + echo "Remounting file system writable" + $adb remount | tee -a $log +} + function sync() { echo "Syncing to device" - $adb remount | tee -a $log $adb sync data | tee -a $log } +# some commands require write access, remount once and for all +remount + # build and sync compile_module "$PWD/OverlayTest/Android.mk" compile_module "$PWD/OverlayTestOverlay/Android.mk" diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index f3a1d9a..8cce191 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -5159,7 +5159,8 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui const uint32_t pkg_id = pkg->package->id << 24; for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) { - ssize_t offset = -1; + ssize_t first = -1; + ssize_t last = -1; const Type* typeConfigs = pkg->getType(typeIndex); ssize_t mapIndex = map.add(); if (mapIndex < 0) { @@ -5167,12 +5168,14 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui } Vector<uint32_t>& vector = map.editItemAt(mapIndex); for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) { - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) + uint32_t resID = pkg_id | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); resource_name resName; if (!this->getResourceName(resID, &resName)) { ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID); + // add dummy value, or trimming leading/trailing zeroes later will fail + vector.push(0); continue; } @@ -5185,13 +5188,13 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui overlayPackage.string(), overlayPackage.size()); if (overlayResID != 0) { - // overlay package has package ID == 0, use original package's ID instead - overlayResID |= pkg_id; + overlayResID = pkg_id | (0x00ffffff & overlayResID); + last = Res_GETENTRY(resID); + if (first == -1) { + first = Res_GETENTRY(resID); + } } vector.push(overlayResID); - if (overlayResID != 0 && offset == -1) { - offset = Res_GETENTRY(resID); - } #if 0 if (overlayResID != 0) { ALOGD("%s/%s 0x%08x -> 0x%08x\n", @@ -5202,13 +5205,16 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui #endif } - if (offset != -1) { - // shave off leading and trailing entries which lack overlay values - vector.removeItemsAt(0, offset); - vector.insertAt((uint32_t)offset, 0, 1); - while (vector.top() == 0) { - vector.pop(); + if (first != -1) { + // shave off trailing entries which lack overlay values + const size_t last_past_one = last + 1; + if (last_past_one < vector.size()) { + vector.removeItemsAt(last_past_one, vector.size() - last_past_one); } + // shave off leading entries which lack overlay values + vector.removeItemsAt(0, first); + // store offset to first overlaid resource ID of this type + vector.insertAt((uint32_t)first, 0, 1); // reserve space for number and offset of entries, and the actual entries *outSize += (2 + vector.size()) * sizeof(uint32_t); } else { @@ -5246,6 +5252,10 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui if (N == 0) { continue; } + if (N == 1) { // vector expected to hold (offset) + (N > 0 entries) + ALOGW("idmap: type %d supposedly has entries, but no entries found\n", i); + return UNKNOWN_ERROR; + } *data++ = htodl(N - 1); // do not count the offset (which is vector's first element) for (size_t j = 0; j < N; ++j) { const uint32_t& overlayResID = vector.itemAt(j); diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index c9bec18..d21ada4 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -172,6 +172,7 @@ public class MediaFile { static { addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg", MtpConstants.FORMAT_MP3); + addFileType("MPGA", FILE_TYPE_MP3, "audio/mpeg", MtpConstants.FORMAT_MP3); addFileType("M4A", FILE_TYPE_M4A, "audio/mp4", MtpConstants.FORMAT_MPEG); addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav", MtpConstants.FORMAT_WAV); addFileType("AMR", FILE_TYPE_AMR, "audio/amr"); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index b0939de..bcd0add 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -1540,6 +1540,12 @@ public class DatabaseHelper extends SQLiteOpenHelper { SystemProperties.get("ro.com.android.dataroaming", "false")) ? 1 : 0); + // Mobile Data default, based on build + loadSetting(stmt, Settings.Secure.MOBILE_DATA, + "true".equalsIgnoreCase( + SystemProperties.get("ro.com.android.mobiledata", + "true")) ? 1 : 0); + loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS, R.bool.def_install_non_market_apps); diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 96f83c6..b0a7c4b 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -294,6 +294,13 @@ public class ImageWallpaper extends WallpaperService { updateWallpaperLocked(); } + if (mBackground == null) { + // If we somehow got to this point after we have last flushed + // the wallpaper, well we really need it to draw again. So + // seems like we need to reload it. Ouch. + updateWallpaperLocked(); + } + SurfaceHolder sh = getSurfaceHolder(); final Rect frame = sh.getSurfaceFrame(); final int dw = frame.width(); @@ -315,7 +322,6 @@ public class ImageWallpaper extends WallpaperService { mLastXTranslation = xPixels; mLastYTranslation = yPixels; - if (mIsHwAccelerated) { if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) { drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 47703c6..0aa3018 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -1666,7 +1666,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { */ private void restorePanelState(SparseArray<Parcelable> icicles) { PanelFeatureState st; - for (int curFeatureId = icicles.size() - 1; curFeatureId >= 0; curFeatureId--) { + int curFeatureId; + for (int i = icicles.size() - 1; i >= 0; i--) { + curFeatureId = icicles.keyAt(i); st = getPanelState(curFeatureId, false /* required */); if (st == null) { // The panel must not have been required, and is currently not around, skip it diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 0f5ad66..4ea3d90 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -1691,6 +1691,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Note: we can only do the wipe via ExternalStorageFormatter if the volume is not emulated. if ((forceExtWipe || wipeExtRequested) && !Environment.isExternalStorageEmulated()) { Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET); + intent.putExtra(ExternalStorageFormatter.EXTRA_ALWAYS_RESET, true); intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME); mWakeLock.acquire(10000); mContext.startService(intent); diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 1482d22..04267a3 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -1221,6 +1221,7 @@ class MountService extends IMountService.Stub for(MountServiceBinderListener bl : mListeners) { if (bl.mListener == listener) { mListeners.remove(mListeners.indexOf(bl)); + listener.asBinder().unlinkToDeath(bl, 0); return; } } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 4390e55..0a9905e 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -1165,6 +1165,8 @@ public class PowerManagerService extends IPowerManager.Stub ? "SCREEN_BRIGHT_BIT " : "") + (((state & SCREEN_ON_BIT) != 0) ? "SCREEN_ON_BIT " : "") + + (((state & BUTTON_BRIGHT_BIT) != 0) + ? "BUTTON_BRIGHT_BIT " : "") + (((state & BATTERY_LOW_BIT) != 0) ? "BATTERY_LOW_BIT " : ""); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index eaecd4c..7e733c6 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -83,7 +83,7 @@ class ServerThread extends Thread { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis()); - Looper.prepare(); + Looper.prepareMainLooper(); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 63455ee..c808930 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1820,7 +1820,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (cr.binding != null && cr.binding.service != null && cr.binding.service.app != null && cr.binding.service.app.lruSeq != mLruSeq) { - updateLruProcessInternalLocked(cr.binding.service.app, oomAdj, + updateLruProcessInternalLocked(cr.binding.service.app, false, updateActivityTime, i+1); } } @@ -1828,7 +1828,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int j=app.conProviders.size()-1; j>=0; j--) { ContentProviderRecord cpr = app.conProviders.get(j).provider; if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) { - updateLruProcessInternalLocked(cpr.proc, oomAdj, + updateLruProcessInternalLocked(cpr.proc, false, updateActivityTime, i+1); } } diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index 91cd458..b26e853 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -1241,7 +1241,7 @@ final class Settings { // Avoid any application that has a space in its path // or that is handled by the system. - if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID) + if (dataPath.indexOf(" ") >= 0 || ai.uid < Process.FIRST_APPLICATION_UID) continue; // we store on each line the following information for now: diff --git a/services/java/com/android/server/pm/ShutdownThread.java b/services/java/com/android/server/pm/ShutdownThread.java index 69406c8..3675d41 100644 --- a/services/java/com/android/server/pm/ShutdownThread.java +++ b/services/java/com/android/server/pm/ShutdownThread.java @@ -84,6 +84,8 @@ public final class ShutdownThread extends Thread { private PowerManager.WakeLock mCpuWakeLock; private PowerManager.WakeLock mScreenWakeLock; private Handler mHandler; + + private static AlertDialog sConfirmDialog; private ShutdownThread() { } @@ -124,7 +126,10 @@ public final class ShutdownThread extends Thread { if (confirm) { final CloseDialogReceiver closer = new CloseDialogReceiver(context); - final AlertDialog dialog = new AlertDialog.Builder(context) + if (sConfirmDialog != null) { + sConfirmDialog.dismiss(); + } + sConfirmDialog = new AlertDialog.Builder(context) .setTitle(mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_title : com.android.internal.R.string.power_off) @@ -136,10 +141,10 @@ public final class ShutdownThread extends Thread { }) .setNegativeButton(com.android.internal.R.string.no, null) .create(); - closer.dialog = dialog; - dialog.setOnDismissListener(closer); - dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - dialog.show(); + closer.dialog = sConfirmDialog; + sConfirmDialog.setOnDismissListener(closer); + sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + sConfirmDialog.show(); } else { beginShutdownSequence(context); } diff --git a/telephony/java/android/telephony/cdma/CdmaCellLocation.java b/telephony/java/android/telephony/cdma/CdmaCellLocation.java index 84db830..6cfae6a 100644 --- a/telephony/java/android/telephony/cdma/CdmaCellLocation.java +++ b/telephony/java/android/telephony/cdma/CdmaCellLocation.java @@ -81,14 +81,26 @@ public class CdmaCellLocation extends CellLocation { } /** - * @return cdma base station latitude, Integer.MAX_VALUE if unknown + * Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0. + * (http://www.3gpp2.org/public_html/specs/C.S0005-A_v6.0.pdf) + * It is represented in units of 0.25 seconds and ranges from -1296000 + * to 1296000, both values inclusive (corresponding to a range of -90 + * to +90 degrees). Integer.MAX_VALUE is considered invalid value. + * + * @return cdma base station latitude in units of 0.25 seconds, Integer.MAX_VALUE if unknown */ public int getBaseStationLatitude() { return this.mBaseStationLatitude; } /** - * @return cdma base station longitude, Integer.MAX_VALUE if unknown + * Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0. + * (http://www.3gpp2.org/public_html/specs/C.S0005-A_v6.0.pdf) + * It is represented in units of 0.25 seconds and ranges from -2592000 + * to 2592000, both values inclusive (corresponding to a range of -180 + * to +180 degrees). Integer.MAX_VALUE is considered invalid value. + * + * @return cdma base station longitude in units of 0.25 seconds, Integer.MAX_VALUE if unknown */ public int getBaseStationLongitude() { return this.mBaseStationLongitude; @@ -215,6 +227,22 @@ public class CdmaCellLocation extends CellLocation { this.mNetworkId == -1); } + /** + * Converts latitude or longitude from 0.25 seconds (as defined in the + * 3GPP2 C.S0005-A v6.0 standard) to decimal degrees + * + * @param quartSec latitude or longitude in 0.25 seconds units + * @return latitude or longitude in decimal degrees units + * @throws IllegalArgumentException if value is less than -2592000, + * greater than 2592000, or is not a number. + */ + public static double convertQuartSecToDecDegrees(int quartSec) { + if(Double.isNaN(quartSec) || quartSec < -2592000 || quartSec > 2592000){ + // Invalid value + throw new IllegalArgumentException("Invalid coordiante value:" + quartSec); + } + return ((double)quartSec) / (3600 * 4); + } } diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java index 07f90cd..021602f 100644 --- a/telephony/java/com/android/internal/telephony/Connection.java +++ b/telephony/java/com/android/internal/telephony/Connection.java @@ -28,6 +28,10 @@ public abstract class Connection { public static int PRESENTATION_UNKNOWN = 3; // no specified or unknown by network public static int PRESENTATION_PAYPHONE = 4; // show pay phone info + //Caller Name Display + protected String cnapName; + protected int cnapNamePresentation = PRESENTATION_ALLOWED; + private static String LOG_TAG = "TelephonyConnection"; public enum DisconnectCause { @@ -84,11 +88,11 @@ public abstract class Connection { public abstract String getAddress(); /** - * Gets CDMA CNAP name associated with connection. + * Gets CNAP name associated with connection. * @return cnap name or null if unavailable */ public String getCnapName() { - return null; + return cnapName; } /** @@ -100,12 +104,12 @@ public abstract class Connection { } /** - * Gets CDMA CNAP presentation associated with connection. + * Gets CNAP presentation associated with connection. * @return cnap name or null if unavailable */ public int getCnapNamePresentation() { - return 0; + return cnapNamePresentation; }; /** diff --git a/telephony/java/com/android/internal/telephony/cat/CommandDetails.java b/telephony/java/com/android/internal/telephony/cat/CommandDetails.java index 8579535..3e7f722 100644 --- a/telephony/java/com/android/internal/telephony/cat/CommandDetails.java +++ b/telephony/java/com/android/internal/telephony/cat/CommandDetails.java @@ -48,13 +48,14 @@ class CommandDetails extends ValueObject implements Parcelable { } public CommandDetails(Parcel in) { - compRequired = true; + compRequired = in.readInt() != 0; commandNumber = in.readInt(); typeOfCommand = in.readInt(); commandQualifier = in.readInt(); } public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(compRequired ? 1 : 0); dest.writeInt(commandNumber); dest.writeInt(typeOfCommand); dest.writeInt(commandQualifier); @@ -111,4 +112,4 @@ class ItemsIconId extends ValueObject { ComprehensionTlvTag getTag() { return ComprehensionTlvTag.ITEM_ICON_ID_LIST; } -}
\ No newline at end of file +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java index 98ad3b1..6985979 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java @@ -51,7 +51,6 @@ public class CdmaConnection extends Connection { String postDialString; // outgoing calls only boolean isIncoming; boolean disconnected; - String cnapName; int index; // index in CdmaCallTracker.connections[], -1 if unassigned /* @@ -77,7 +76,6 @@ public class CdmaConnection extends Connection { DisconnectCause cause = DisconnectCause.NOT_DISCONNECTED; PostDialState postDialState = PostDialState.NOT_STARTED; int numberPresentation = Connection.PRESENTATION_ALLOWED; - int cnapNamePresentation = Connection.PRESENTATION_ALLOWED; Handler h; @@ -230,14 +228,6 @@ public class CdmaConnection extends Connection { return address; } - public String getCnapName() { - return cnapName; - } - - public int getCnapNamePresentation() { - return cnapNamePresentation; - } - public CdmaCall getCall() { return parent; } diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index 6e9cd51..484bab0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -1232,7 +1232,7 @@ public class GSMPhone extends PhoneBase { // If the radio shuts off or resets while one of these // is pending, we need to clean up. - for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) { + for (int i = mPendingMMIs.size() - 1; i >= 0; i--) { if (mPendingMMIs.get(i).isPendingUSSD()) { mPendingMMIs.get(i).onUssdFinishedError(); } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java index f7c6025..9e32e8d 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java @@ -26,6 +26,7 @@ import android.os.SystemClock; import android.util.Log; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; +import android.text.TextUtils; import com.android.internal.telephony.*; @@ -125,6 +126,8 @@ public class GsmConnection extends Connection { isIncoming = dc.isMT; createTime = System.currentTimeMillis(); + cnapName = dc.name; + cnapNamePresentation = dc.namePresentation; numberPresentation = dc.numberPresentation; uusInfo = dc.uusInfo; @@ -151,6 +154,9 @@ public class GsmConnection extends Connection { index = -1; isIncoming = false; + cnapName = null; + cnapNamePresentation = Connection.PRESENTATION_ALLOWED; + numberPresentation = Connection.PRESENTATION_ALLOWED; createTime = System.currentTimeMillis(); this.parent = parent; @@ -437,6 +443,21 @@ public class GsmConnection extends Connection { changed = true; } + // A null cnapName should be the same as "" + if (TextUtils.isEmpty(dc.name)) { + if (!TextUtils.isEmpty(cnapName)) { + changed = true; + cnapName = ""; + } + } else if (!dc.name.equals(cnapName)) { + changed = true; + cnapName = dc.name; + } + + if (Phone.DEBUG_PHONE) log("--dssds----"+cnapName); + cnapNamePresentation = dc.namePresentation; + numberPresentation = dc.numberPresentation; + if (newParent != parent) { if (parent != null) { parent.detach(this); diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index f2fa3f5..46b8a27 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -58,7 +58,7 @@ static bool validateFileName(const char* fileName) // The default to use if no other ignore pattern is defined. const char * const gDefaultIgnoreAssets = - "!.svn:!.git:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~"; + "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"; // The ignore pattern that can be passed via --ignore-assets in Main.cpp const char * gUserIgnoreAssets = NULL; diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index d0a81dc..482f43e 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -29,6 +29,10 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS += -Wno-format-y2k +ifeq (darwin,$(HOST_OS)) +LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS +endif + LOCAL_C_INCLUDES += external/expat/lib LOCAL_C_INCLUDES += external/libpng diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index daeadc0..d7cb61a 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -53,7 +53,6 @@ public: mWantUTF16(false), mValues(false), mCompressionMethod(0), mOutputAPKFile(NULL), mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL), - mIsOverlayPackage(false), mAutoAddOverlay(false), mGenDependencies(false), mAssetSourceDir(NULL), mCrunchedOutputDir(NULL), mProguardFile(NULL), @@ -107,8 +106,6 @@ public: void setManifestPackageNameOverride(const char * val) { mManifestPackageNameOverride = val; } const char* getInstrumentationPackageNameOverride() const { return mInstrumentationPackageNameOverride; } void setInstrumentationPackageNameOverride(const char * val) { mInstrumentationPackageNameOverride = val; } - bool getIsOverlayPackage() const { return mIsOverlayPackage; } - void setIsOverlayPackage(bool val) { mIsOverlayPackage = val; } bool getAutoAddOverlay() { return mAutoAddOverlay; } void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; } bool getGenDependencies() { return mGenDependencies; } @@ -250,7 +247,6 @@ private: const char* mOutputAPKFile; const char* mManifestPackageNameOverride; const char* mInstrumentationPackageNameOverride; - bool mIsOverlayPackage; bool mAutoAddOverlay; bool mGenDependencies; const char* mAssetSourceDir; diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 9f05c6a..9570c66 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -69,7 +69,6 @@ void usage(void) " [-F apk-file] [-J R-file-dir] \\\n" " [--product product1,product2,...] \\\n" " [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n" - " [-o] \\\n" " [raw-files-dir [raw-files-dir] ...]\n" "\n" " Package the android resources. It will read assets and resources that are\n" @@ -110,7 +109,6 @@ void usage(void) " -j specify a jar or zip file containing classes to include\n" " -k junk path of file(s) added\n" " -m make package directories under location specified by -J\n" - " -o create overlay package (ie only resources; expects <overlay-package> in manifest)\n" #if 0 " -p pseudolocalize the default configuration\n" #endif @@ -296,9 +294,6 @@ int main(int argc, char* const argv[]) case 'm': bundle.setMakePackageDirs(true); break; - case 'o': - bundle.setIsOverlayPackage(true); - break; #if 0 case 'p': bundle.setPseudolocalize(true); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 0195727..d98fe65 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -2749,6 +2749,12 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) const bool filterable = (typeName != mipmap16); const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0; + + // Until a non-NO_ENTRY value has been written for a resource, + // that resource is invalid; validResources[i] represents + // the item at t->getOrderedConfigs().itemAt(i). + Vector<bool> validResources; + validResources.insertAt(false, 0, N); // First write the typeSpec chunk, containing information about // each resource entry in this type. @@ -2885,6 +2891,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) if (amt < 0) { return amt; } + validResources.editItemAt(ei) = true; } else { index[ei] = htodl(ResTable_type::NO_ENTRY); } @@ -2895,6 +2902,14 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) (((uint8_t*)data->editData()) + typeStart); tHeader->header.size = htodl(data->getSize()-typeStart); } + + for (size_t i = 0; i < N; ++i) { + if (!validResources[i]) { + sp<ConfigList> c = t->getOrderedConfigs().itemAt(i); + fprintf(stderr, "warning: no entries written for %s/%s\n", + String8(typeName).string(), String8(c->getName()).string()); + } + } } // Fill in the rest of the package information. @@ -3723,9 +3738,7 @@ sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package) { sp<Package> p = mPackages.valueFor(package); if (p == NULL) { - if (mBundle->getIsOverlayPackage()) { - p = new Package(package, 0x00); - } else if (mIsAppPackage) { + if (mIsAppPackage) { if (mHaveAppPackage) { fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n" "Use -x to create extended resources.\n"); diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath index 9fb000e..a5db7b1 100644 --- a/tools/layoutlib/bridge/.classpath +++ b/tools/layoutlib/bridge/.classpath @@ -3,7 +3,7 @@ <classpathentry excluding="org/kxml2/io/" kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/layoutlib_api/layoutlib_api-prebuilt.jar"/> - <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/> + <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/ninepatch/ninepatch-prebuilt.jar"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/tools-common/tools-common-prebuilt.jar"/> diff --git a/tools/layoutlib/bridge/tests/.classpath b/tools/layoutlib/bridge/tests/.classpath index 027bc67..2b32e09 100644 --- a/tools/layoutlib/bridge/tests/.classpath +++ b/tools/layoutlib/bridge/tests/.classpath @@ -4,7 +4,7 @@ <classpathentry kind="src" path="res"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/layoutlib_bridge"/> - <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/> + <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> <classpathentry kind="output" path="bin"/> diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java new file mode 100755 index 0000000..c988c70 --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java @@ -0,0 +1,787 @@ +/* + * Copyright (C) 2012 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.tools.layoutlib.create; + +import com.android.tools.layoutlib.annotations.VisibleForTesting; +import com.android.tools.layoutlib.annotations.VisibleForTesting.Visibility; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.signature.SignatureReader; +import org.objectweb.asm.signature.SignatureVisitor; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Analyzes the input JAR using the ASM java bytecode manipulation library + * to list the classes and their dependencies. A "dependency" is a class + * used by another class. + */ +public class DependencyFinder { + + // Note: a bunch of stuff has package-level access for unit tests. Consider it private. + + /** Output logger. */ + private final Log mLog; + + /** + * Creates a new analyzer. + * + * @param log The log output. + */ + public DependencyFinder(Log log) { + mLog = log; + } + + /** + * Starts the analysis using parameters from the constructor. + * + * @param osJarPath The input source JARs to parse. + * @return A pair: [0]: map { class FQCN => set of FQCN class dependencies }. + * [1]: map { missing class FQCN => set of FQCN class that uses it. } + */ + public List<Map<String, Set<String>>> findDeps(List<String> osJarPath) throws IOException { + + Map<String, ClassReader> zipClasses = parseZip(osJarPath); + mLog.info("Found %d classes in input JAR%s.", + zipClasses.size(), + osJarPath.size() > 1 ? "s" : ""); + + Map<String, Set<String>> deps = findClassesDeps(zipClasses); + + Map<String, Set<String>> missing = findMissingClasses(deps, zipClasses.keySet()); + + List<Map<String, Set<String>>> result = new ArrayList<Map<String,Set<String>>>(2); + result.add(deps); + result.add(missing); + return result; + } + + /** + * Prints dependencies to the current logger, found stuff and missing stuff. + */ + public void printAllDeps(List<Map<String, Set<String>>> result) { + assert result.size() == 2; + Map<String, Set<String>> deps = result.get(0); + Map<String, Set<String>> missing = result.get(1); + + // Print all dependences found in the format: + // +Found: <FQCN from zip> + // uses: FQCN + + mLog.info("++++++ %d Entries found in source JARs", deps.size()); + mLog.info(""); + + for (Entry<String, Set<String>> entry : deps.entrySet()) { + mLog.info( "+Found : %s", entry.getKey()); + for (String dep : entry.getValue()) { + mLog.info(" uses: %s", dep); + } + + mLog.info(""); + } + + + // Now print all missing dependences in the format: + // -Missing <FQCN>: + // used by: <FQCN> + + mLog.info(""); + mLog.info("------ %d Entries missing from source JARs", missing.size()); + mLog.info(""); + + for (Entry<String, Set<String>> entry : missing.entrySet()) { + mLog.info( "-Missing : %s", entry.getKey()); + for (String dep : entry.getValue()) { + mLog.info(" used by: %s", dep); + } + + mLog.info(""); + } + } + + /** + * Prints only a summary of the missing dependencies to the current logger. + */ + public void printMissingDeps(List<Map<String, Set<String>>> result) { + assert result.size() == 2; + @SuppressWarnings("unused") Map<String, Set<String>> deps = result.get(0); + Map<String, Set<String>> missing = result.get(1); + + for (String fqcn : missing.keySet()) { + mLog.info("%s", fqcn); + } + } + + // ---------------- + + /** + * Parses a JAR file and returns a list of all classes founds using a map + * class name => ASM ClassReader. Class names are in the form "android.view.View". + */ + Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException { + TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>(); + + for (String jarPath : jarPathList) { + ZipFile zip = new ZipFile(jarPath); + Enumeration<? extends ZipEntry> entries = zip.entries(); + ZipEntry entry; + while (entries.hasMoreElements()) { + entry = entries.nextElement(); + if (entry.getName().endsWith(".class")) { + ClassReader cr = new ClassReader(zip.getInputStream(entry)); + String className = classReaderToClassName(cr); + classes.put(className, cr); + } + } + } + + return classes; + } + + /** + * Utility that returns the fully qualified binary class name for a ClassReader. + * E.g. it returns something like android.view.View. + */ + static String classReaderToClassName(ClassReader classReader) { + if (classReader == null) { + return null; + } else { + return classReader.getClassName().replace('/', '.'); + } + } + + /** + * Utility that returns the fully qualified binary class name from a path-like FQCN. + * E.g. it returns android.view.View from android/view/View. + */ + static String internalToBinaryClassName(String className) { + if (className == null) { + return null; + } else { + return className.replace('/', '.'); + } + } + + /** + * Finds all dependencies for all classes in keepClasses which are also + * listed in zipClasses. Returns a map of all the dependencies found. + */ + Map<String, Set<String>> findClassesDeps(Map<String, ClassReader> zipClasses) { + + // The dependencies that we'll collect. + // It's a map Class name => uses class names. + Map<String, Set<String>> dependencyMap = new TreeMap<String, Set<String>>(); + + DependencyVisitor visitor = getVisitor(); + + int count = 0; + try { + for (Entry<String, ClassReader> entry : zipClasses.entrySet()) { + String name = entry.getKey(); + + TreeSet<String> set = new TreeSet<String>(); + dependencyMap.put(name, set); + visitor.setDependencySet(set); + + ClassReader cr = entry.getValue(); + cr.accept(visitor, 0 /* flags */); + + visitor.setDependencySet(null); + + mLog.debugNoln("Visited %d classes\r", ++count); + } + } finally { + mLog.debugNoln("\n"); + } + + return dependencyMap; + } + + /** + * Computes which classes FQCN were found as dependencies that are NOT listed + * in the original JAR classes. + * + * @param deps The map { FQCN => dependencies[] } returned by {@link #findClassesDeps(Map)}. + * @param zipClasses The set of all classes FQCN found in the JAR files. + * @return A map { FQCN not found in the zipClasses => classes using it } + */ + private Map<String, Set<String>> findMissingClasses( + Map<String, Set<String>> deps, + Set<String> zipClasses) { + Map<String, Set<String>> missing = new TreeMap<String, Set<String>>(); + + for (Entry<String, Set<String>> entry : deps.entrySet()) { + String name = entry.getKey(); + + for (String dep : entry.getValue()) { + if (!zipClasses.contains(dep)) { + // This dependency doesn't exist in the zip classes. + Set<String> set = missing.get(dep); + if (set == null) { + set = new TreeSet<String>(); + missing.put(dep, set); + } + set.add(name); + } + } + + } + + return missing; + } + + + // ---------------------------------- + + /** + * Instantiates a new DependencyVisitor. Useful for unit tests. + */ + @VisibleForTesting(visibility=Visibility.PRIVATE) + DependencyVisitor getVisitor() { + return new DependencyVisitor(); + } + + /** + * Visitor to collect all the type dependencies from a class. + */ + public class DependencyVisitor extends ClassVisitor { + + private Set<String> mCurrentDepSet; + + /** + * Creates a new visitor that will find all the dependencies for the visited class. + */ + public DependencyVisitor() { + super(Opcodes.ASM4); + } + + /** + * Sets the {@link Set} where to record direct dependencies for this class. + * This will change before each {@link ClassReader#accept(ClassVisitor, int)} call. + */ + public void setDependencySet(Set<String> set) { + mCurrentDepSet = set; + } + + /** + * Considers the given class name as a dependency. + */ + public void considerName(String className) { + if (className == null) { + return; + } + + className = internalToBinaryClassName(className); + + try { + // exclude classes that are part of the default JRE (the one executing this program) + if (getClass().getClassLoader().loadClass(className) != null) { + return; + } + } catch (ClassNotFoundException e) { + // ignore + } + + // Add it to the dependency set for the currently visited class, as needed. + assert mCurrentDepSet != null; + if (mCurrentDepSet != null) { + mCurrentDepSet.add(className); + } + } + + /** + * Considers this array of names using considerName(). + */ + public void considerNames(String[] classNames) { + if (classNames != null) { + for (String className : classNames) { + considerName(className); + } + } + } + + /** + * Considers this signature or type signature by invoking the {@link SignatureVisitor} + * on it. + */ + public void considerSignature(String signature) { + if (signature != null) { + SignatureReader sr = new SignatureReader(signature); + // SignatureReader.accept will call accessType so we don't really have + // to differentiate where the signature comes from. + sr.accept(new MySignatureVisitor()); + } + } + + /** + * Considers this {@link Type}. For arrays, the element type is considered. + * If the type is an object, it's internal name is considered. + */ + public void considerType(Type t) { + if (t != null) { + if (t.getSort() == Type.ARRAY) { + t = t.getElementType(); + } + if (t.getSort() == Type.OBJECT) { + considerName(t.getInternalName()); + } + } + } + + /** + * Considers a descriptor string. The descriptor is converted to a {@link Type} + * and then considerType() is invoked. + */ + public boolean considerDesc(String desc) { + if (desc != null) { + try { + if (desc.length() > 0 && desc.charAt(0) == '(') { + // This is a method descriptor with arguments and a return type. + Type t = Type.getReturnType(desc); + considerType(t); + + for (Type arg : Type.getArgumentTypes(desc)) { + considerType(arg); + } + + } else { + Type t = Type.getType(desc); + considerType(t); + } + return true; + } catch (ArrayIndexOutOfBoundsException e) { + // ignore, not a valid type. + } + } + return false; + } + + + // --------------------------------------------------- + // --- ClassVisitor, FieldVisitor + // --------------------------------------------------- + + // Visits a class header + @Override + public void visit(int version, int access, String name, + String signature, String superName, String[] interfaces) { + // signature is the signature of this class. May be null if the class is not a generic + // one, and does not extend or implement generic classes or interfaces. + + if (signature != null) { + considerSignature(signature); + } + + // superName is the internal of name of the super class (see getInternalName). + // For interfaces, the super class is Object. May be null but only for the Object class. + considerName(superName); + + // interfaces is the internal names of the class's interfaces (see getInternalName). + // May be null. + considerNames(interfaces); + } + + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + // desc is the class descriptor of the annotation class. + considerDesc(desc); + return new MyAnnotationVisitor(); + } + + @Override + public void visitAttribute(Attribute attr) { + // pass + } + + // Visits the end of a class + @Override + public void visitEnd() { + // pass + } + + private class MyFieldVisitor extends FieldVisitor { + + public MyFieldVisitor() { + super(Opcodes.ASM4); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + // desc is the class descriptor of the annotation class. + considerDesc(desc); + return new MyAnnotationVisitor(); + } + + @Override + public void visitAttribute(Attribute attr) { + // pass + } + + // Visits the end of a class + @Override + public void visitEnd() { + // pass + } + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, + String signature, Object value) { + // desc is the field's descriptor (see Type). + considerDesc(desc); + + // signature is the field's signature. May be null if the field's type does not use + // generic types. + considerSignature(signature); + + return new MyFieldVisitor(); + } + + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + // name is the internal name of an inner class (see getInternalName). + // Note: outerName/innerName seems to be null when we're reading the + // _Original_ClassName classes generated by layoutlib_create. + if (outerName != null) { + considerName(name); + } + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, + String signature, String[] exceptions) { + // desc is the method's descriptor (see Type). + considerDesc(desc); + // signature is the method's signature. May be null if the method parameters, return + // type and exceptions do not use generic types. + considerSignature(signature); + + return new MyMethodVisitor(); + } + + @Override + public void visitOuterClass(String owner, String name, String desc) { + // pass + } + + @Override + public void visitSource(String source, String debug) { + // pass + } + + + // --------------------------------------------------- + // --- MethodVisitor + // --------------------------------------------------- + + private class MyMethodVisitor extends MethodVisitor { + + public MyMethodVisitor() { + super(Opcodes.ASM4); + } + + + @Override + public AnnotationVisitor visitAnnotationDefault() { + return new MyAnnotationVisitor(); + } + + @Override + public void visitCode() { + // pass + } + + // field instruction + @Override + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + // name is the field's name. + // desc is the field's descriptor (see Type). + considerDesc(desc); + } + + @Override + public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) { + // pass + } + + @Override + public void visitIincInsn(int var, int increment) { + // pass -- an IINC instruction + } + + @Override + public void visitInsn(int opcode) { + // pass -- a zero operand instruction + } + + @Override + public void visitIntInsn(int opcode, int operand) { + // pass -- a single int operand instruction + } + + @Override + public void visitJumpInsn(int opcode, Label label) { + // pass -- a jump instruction + } + + @Override + public void visitLabel(Label label) { + // pass -- a label target + } + + // instruction to load a constant from the stack + @Override + public void visitLdcInsn(Object cst) { + if (cst instanceof Type) { + considerType((Type) cst); + } + } + + @Override + public void visitLineNumber(int line, Label start) { + // pass + } + + @Override + public void visitLocalVariable(String name, String desc, + String signature, Label start, Label end, int index) { + // desc is the type descriptor of this local variable. + considerDesc(desc); + // signature is the type signature of this local variable. May be null if the local + // variable type does not use generic types. + considerSignature(signature); + } + + @Override + public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + // pass -- a lookup switch instruction + } + + @Override + public void visitMaxs(int maxStack, int maxLocals) { + // pass + } + + // instruction that invokes a method + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc) { + + // owner is the internal name of the method's owner class + if (!considerDesc(owner) && owner.indexOf('/') != -1) { + considerName(owner); + } + // desc is the method's descriptor (see Type). + considerDesc(desc); + } + + // instruction multianewarray, whatever that is + @Override + public void visitMultiANewArrayInsn(String desc, int dims) { + + // desc an array type descriptor. + considerDesc(desc); + } + + @Override + public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, + boolean visible) { + // desc is the class descriptor of the annotation class. + considerDesc(desc); + return new MyAnnotationVisitor(); + } + + @Override + public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { + // pass -- table switch instruction + + } + + @Override + public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { + // type is the internal name of the type of exceptions handled by the handler, + // or null to catch any exceptions (for "finally" blocks). + considerName(type); + } + + // type instruction + @Override + public void visitTypeInsn(int opcode, String type) { + // type is the operand of the instruction to be visited. This operand must be the + // internal name of an object or array class. + considerName(type); + } + + @Override + public void visitVarInsn(int opcode, int var) { + // pass -- local variable instruction + } + } + + private class MySignatureVisitor extends SignatureVisitor { + + public MySignatureVisitor() { + super(Opcodes.ASM4); + } + + // --------------------------------------------------- + // --- SignatureVisitor + // --------------------------------------------------- + + private String mCurrentSignatureClass = null; + + // Starts the visit of a signature corresponding to a class or interface type + @Override + public void visitClassType(String name) { + mCurrentSignatureClass = name; + considerName(name); + } + + // Visits an inner class + @Override + public void visitInnerClassType(String name) { + if (mCurrentSignatureClass != null) { + mCurrentSignatureClass += "$" + name; + considerName(mCurrentSignatureClass); + } + } + + @Override + public SignatureVisitor visitArrayType() { + return new MySignatureVisitor(); + } + + @Override + public void visitBaseType(char descriptor) { + // pass -- a primitive type, ignored + } + + @Override + public SignatureVisitor visitClassBound() { + return new MySignatureVisitor(); + } + + @Override + public SignatureVisitor visitExceptionType() { + return new MySignatureVisitor(); + } + + @Override + public void visitFormalTypeParameter(String name) { + // pass + } + + @Override + public SignatureVisitor visitInterface() { + return new MySignatureVisitor(); + } + + @Override + public SignatureVisitor visitInterfaceBound() { + return new MySignatureVisitor(); + } + + @Override + public SignatureVisitor visitParameterType() { + return new MySignatureVisitor(); + } + + @Override + public SignatureVisitor visitReturnType() { + return new MySignatureVisitor(); + } + + @Override + public SignatureVisitor visitSuperclass() { + return new MySignatureVisitor(); + } + + @Override + public SignatureVisitor visitTypeArgument(char wildcard) { + return new MySignatureVisitor(); + } + + @Override + public void visitTypeVariable(String name) { + // pass + } + + @Override + public void visitTypeArgument() { + // pass + } + } + + + // --------------------------------------------------- + // --- AnnotationVisitor + // --------------------------------------------------- + + private class MyAnnotationVisitor extends AnnotationVisitor { + + public MyAnnotationVisitor() { + super(Opcodes.ASM4); + } + + // Visits a primitive value of an annotation + @Override + public void visit(String name, Object value) { + // value is the actual value, whose type must be Byte, Boolean, Character, Short, + // Integer, Long, Float, Double, String or Type + if (value instanceof Type) { + considerType((Type) value); + } + } + + @Override + public AnnotationVisitor visitAnnotation(String name, String desc) { + // desc is the class descriptor of the nested annotation class. + considerDesc(desc); + return new MyAnnotationVisitor(); + } + + @Override + public AnnotationVisitor visitArray(String name) { + return new MyAnnotationVisitor(); + } + + @Override + public void visitEnum(String name, String desc, String value) { + // desc is the class descriptor of the enumeration class. + considerDesc(desc); + } + } + } +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java index 8efd871..c3ba591 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java @@ -33,11 +33,19 @@ public class Log { } } + /** Similar to debug() but doesn't do a \n automatically. */ + public void debugNoln(String format, Object... args) { + if (mVerbose) { + String s = String.format(format, args); + System.out.print(s); + } + } + public void info(String format, Object... args) { String s = String.format(format, args); outPrintln(s); } - + public void error(String format, Object... args) { String s = String.format(format, args); errPrintln(s); @@ -50,15 +58,15 @@ public class Log { pw.flush(); error(format + "\n" + sw.toString(), args); } - + /** for unit testing */ protected void errPrintln(String msg) { System.err.println(msg); } - + /** for unit testing */ protected void outPrintln(String msg) { System.out.println(msg); } - + } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index 4b7a348..9cd74db 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -18,6 +18,8 @@ package com.android.tools.layoutlib.create; import java.io.IOException; import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.Set; @@ -47,6 +49,8 @@ public class Main { public static class Options { public boolean generatePublicAccess = true; + public boolean listAllDeps = false; + public boolean listOnlyMissingDeps = false; } public static final Options sOptions = new Options(); @@ -60,16 +64,29 @@ public class Main { if (!processArgs(log, args, osJarPath, osDestJar)) { log.error("Usage: layoutlib_create [-v] [-p] output.jar input.jar ..."); + log.error("Usage: layoutlib_create [-v] [--list-deps|--missing-deps] input.jar ..."); System.exit(1); } - log.info("Output: %1$s", osDestJar[0]); + if (sOptions.listAllDeps || sOptions.listOnlyMissingDeps) { + System.exit(listDeps(osJarPath, log)); + + } else { + System.exit(createLayoutLib(osDestJar[0], osJarPath, log)); + } + + + System.exit(1); + } + + private static int createLayoutLib(String osDestJar, ArrayList<String> osJarPath, Log log) { + log.info("Output: %1$s", osDestJar); for (String path : osJarPath) { log.info("Input : %1$s", path); } try { - AsmGenerator agen = new AsmGenerator(log, osDestJar[0], new CreateInfo()); + AsmGenerator agen = new AsmGenerator(log, osDestJar, new CreateInfo()); AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen, new String[] { // derived from @@ -116,17 +133,33 @@ public class Main { for (String path : osJarPath) { log.info("- Input JAR : %1$s", path); } - System.exit(1); + return 1; } - System.exit(0); + return 0; } catch (IOException e) { log.exception(e, "Failed to load jar"); } catch (LogAbortException e) { e.error(log); } - System.exit(1); + return 1; + } + + private static int listDeps(ArrayList<String> osJarPath, Log log) { + DependencyFinder df = new DependencyFinder(log); + try { + List<Map<String, Set<String>>> result = df.findDeps(osJarPath); + if (sOptions.listAllDeps) { + df.printAllDeps(result); + } else if (sOptions.listOnlyMissingDeps) { + df.printMissingDeps(result); + } + } catch (IOException e) { + log.exception(e, "Failed to load jar"); + } + + return 0; } /** @@ -138,14 +171,21 @@ public class Main { */ private static boolean processArgs(Log log, String[] args, ArrayList<String> osJarPath, String[] osDestJar) { + boolean needs_dest = true; for (int i = 0; i < args.length; i++) { String s = args[i]; if (s.equals("-v")) { log.setVerbose(true); } else if (s.equals("-p")) { sOptions.generatePublicAccess = false; + } else if (s.equals("--list-deps")) { + sOptions.listAllDeps = true; + needs_dest = false; + } else if (s.equals("--missing-deps")) { + sOptions.listOnlyMissingDeps = true; + needs_dest = false; } else if (!s.startsWith("-")) { - if (osDestJar[0] == null) { + if (needs_dest && osDestJar[0] == null) { osDestJar[0] = s; } else { osJarPath.add(s); @@ -160,7 +200,7 @@ public class Main { log.error("Missing parameter: path to input jar"); return false; } - if (osDestJar[0] == null) { + if (needs_dest && osDestJar[0] == null) { log.error("Missing parameter: path to output jar"); return false; } |