Merge "Correct executable bit for source files [Take 2]"
diff --git a/api/current.txt b/api/current.txt
index 05ae8ae..e46dbeb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29496,8 +29496,8 @@
     method public void setFormat12Hour(java.lang.CharSequence);
     method public void setFormat24Hour(java.lang.CharSequence);
     method public void setTimeZone(java.lang.String);
-    field public static final java.lang.CharSequence DEFAULT_FORMAT_12_HOUR;
-    field public static final java.lang.CharSequence DEFAULT_FORMAT_24_HOUR;
+    field public static final deprecated java.lang.CharSequence DEFAULT_FORMAT_12_HOUR;
+    field public static final deprecated java.lang.CharSequence DEFAULT_FORMAT_24_HOUR;
   }
 
   public class TextSwitcher extends android.widget.ViewSwitcher {
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 8e1b5f3..09d6f89 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -28,7 +28,7 @@
 dir_rec_t android_media_dir;
 dir_rec_array_t android_system_dirs;
 
-int install(const char *pkgname, uid_t uid, gid_t gid)
+int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo)
 {
     char pkgdir[PKG_PATH_MAX];
     char libsymlink[PKG_PATH_MAX];
@@ -91,7 +91,7 @@
         return -1;
     }
 
-    if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) {
+    if (selinux_android_setfilecon2(pkgdir, pkgname, seinfo, uid) < 0) {
         ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));
         unlink(libsymlink);
         unlink(pkgdir);
@@ -184,7 +184,7 @@
     return delete_dir_contents(pkgdir, 0, "lib");
 }
 
-int make_user_data(const char *pkgname, uid_t uid, uid_t persona)
+int make_user_data(const char *pkgname, uid_t uid, uid_t persona, const char* seinfo)
 {
     char pkgdir[PKG_PATH_MAX];
     char applibdir[PKG_PATH_MAX];
@@ -245,7 +245,7 @@
         return -1;
     }
 
-    if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) {
+    if (selinux_android_setfilecon2(pkgdir, pkgname, seinfo, uid) < 0) {
         ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));
         unlink(libsymlink);
         unlink(pkgdir);
@@ -317,7 +317,7 @@
                 uid = (uid_t) s.st_uid % PER_USER_RANGE;
                 /* Create the directory for the target */
                 make_user_data(name, uid + target_persona * PER_USER_RANGE,
-                               target_persona);
+                               target_persona, NULL);
             }
         }
         closedir(d);
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index c321e5f..281aaab 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -31,7 +31,7 @@
 
 static int do_install(char **arg, char reply[REPLY_MAX])
 {
-    return install(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, gid */
+    return install(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); /* pkgname, uid, gid, seinfo */
 }
 
 static int do_dexopt(char **arg, char reply[REPLY_MAX])
@@ -103,7 +103,8 @@
 
 static int do_mk_user_data(char **arg, char reply[REPLY_MAX])
 {
-    return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, userid */
+    return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]);
+                             /* pkgname, uid, userid, seinfo */
 }
 
 static int do_rm_user(char **arg, char reply[REPLY_MAX])
@@ -134,7 +135,7 @@
 
 struct cmdinfo cmds[] = {
     { "ping",                 0, do_ping },
-    { "install",              3, do_install },
+    { "install",              4, do_install },
     { "dexopt",               3, do_dexopt },
     { "movedex",              2, do_move_dex },
     { "rmdex",                1, do_rm_dex },
@@ -147,7 +148,7 @@
     { "rmuserdata",           2, do_rm_user_data },
     { "movefiles",            0, do_movefiles },
     { "linklib",              3, do_linklib },
-    { "mkuserdata",           3, do_mk_user_data },
+    { "mkuserdata",           4, do_mk_user_data },
     { "rmuser",               1, do_rm_user },
     { "cloneuserdata",        3, do_clone_user_data },
 };
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 0500c23..04498ef 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -192,12 +192,12 @@
 
 /* commands.c */
 
-int install(const char *pkgname, uid_t uid, gid_t gid);
+int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo);
 int uninstall(const char *pkgname, uid_t persona);
 int renamepkg(const char *oldpkgname, const char *newpkgname);
 int fix_uid(const char *pkgname, uid_t uid, gid_t gid);
 int delete_user_data(const char *pkgname, uid_t persona);
-int make_user_data(const char *pkgname, uid_t uid, uid_t persona);
+int make_user_data(const char *pkgname, uid_t uid, uid_t persona, const char* seinfo);
 int delete_persona(uid_t persona);
 int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy);
 int delete_cache(const char *pkgname, uid_t persona);
diff --git a/cmds/interrupter/Android.mk b/cmds/interrupter/Android.mk
new file mode 100644
index 0000000..e324627
--- /dev/null
+++ b/cmds/interrupter/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    interrupter.c
+LOCAL_MODULE := interrupter
+LOCAL_MODULE_TAGS := eng tests
+LOCAL_LDFLAGS := -ldl
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    interrupter.c
+LOCAL_MODULE := interrupter
+LOCAL_MODULE_TAGS := eng tests
+LOCAL_LDFLAGS := -ldl
+
+include $(BUILD_HOST_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/cmds/interrupter/interrupter.c b/cmds/interrupter/interrupter.c
new file mode 100644
index 0000000..ae55515
--- /dev/null
+++ b/cmds/interrupter/interrupter.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 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.
+ */
+
+
+/**
+ * The probability of a syscall failing from 0.0 to 1.0
+ */
+#define PROBABILITY 0.9
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/* for various intercepted calls */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* For builds on glibc */
+#define __USE_GNU
+#include <dlfcn.h>
+
+#include "interrupter.h"
+
+static int probability = PROBABILITY * RAND_MAX;
+
+static int maybe_interrupt() {
+    if (rand() < probability) {
+        return 1;
+    }
+    return 0;
+}
+
+DEFINE_INTERCEPT(read, ssize_t, int, void*, size_t);
+DEFINE_INTERCEPT(write, ssize_t, int, const void*, size_t);
+DEFINE_INTERCEPT(accept, int, int, struct sockaddr*, socklen_t*);
+DEFINE_INTERCEPT(creat, int, const char*, mode_t);
diff --git a/cmds/interrupter/interrupter.h b/cmds/interrupter/interrupter.h
new file mode 100644
index 0000000..9ad0277e
--- /dev/null
+++ b/cmds/interrupter/interrupter.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 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.
+ */
+
+#define CONCATENATE(arg1, arg2)   CONCATENATE1(arg1, arg2)
+#define CONCATENATE1(arg1, arg2)  CONCATENATE2(arg1, arg2)
+#define CONCATENATE2(arg1, arg2)  arg1##arg2
+
+#define INTERRUPTER(sym) \
+    if (real_##sym == NULL) \
+        __init_##sym(); \
+    if (maybe_interrupt()) { \
+        errno = EINTR; \
+        return -1; \
+    }
+
+#define CALL_FUNCTION_1(sym, ret, type1) \
+ret (*real_##sym)(type1) = NULL; \
+ret sym(type1 arg1) { \
+    INTERRUPTER(sym) \
+    return real_##sym(arg1); \
+}
+
+#define CALL_FUNCTION_2(sym, ret, type1, type2) \
+ret (*real_##sym)(type1, type2) = NULL; \
+ret sym(type1 arg1, type2 arg2) { \
+    INTERRUPTER(sym) \
+    return real_##sym(arg1, arg2); \
+}
+
+#define CALL_FUNCTION_3(sym, ret, type1, type2, type3) \
+ret (*real_##sym)(type1, type2, type3) = NULL; \
+ret sym(type1 arg1, type2 arg2, type3 arg3) { \
+    INTERRUPTER(sym) \
+    return real_##sym(arg1, arg2, arg3); \
+}
+
+#define CALL_FUNCTION_4(sym, ret, type1, type2, type3, type4) \
+ret (*real_##sym)(type1, type2, type3, type4) = NULL; \
+ret sym(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+    INTERRUPTER(sym) \
+    return real_##sym(arg1, arg2, arg3, arg4); \
+}
+
+#define CALL_FUNCTION_5(sym, ret, type1, type2, type3, type4, type5) \
+ret (*real_##sym)(type1, type2, type3, type4, type5) = NULL; \
+ret sym(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \
+    INTERRUPTER(sym) \
+    return real_##sym(arg1, arg2, arg3, arg4, arg5); \
+}
+
+#define DEFINE_INTERCEPT_N(N, sym, ret, ...) \
+static void __init_##sym(void); \
+CONCATENATE(CALL_FUNCTION_, N)(sym, ret, __VA_ARGS__) \
+static void __init_##sym(void) { \
+    real_##sym = dlsym(RTLD_NEXT, #sym); \
+    if (real_##sym == NULL) { \
+        fprintf(stderr, "Error hooking " #sym ": %s\n", dlerror()); \
+    } \
+}
+
+#define INTERCEPT_NARG(...) INTERCEPT_NARG_N(__VA_ARGS__, INTERCEPT_RSEQ_N())
+#define INTERCEPT_NARG_N(...) INTERCEPT_ARG_N(__VA_ARGS__)
+#define INTERCEPT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
+#define INTERCEPT_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0
+
+#define DEFINE_INTERCEPT(sym, ret, ...) DEFINE_INTERCEPT_N(INTERCEPT_NARG(__VA_ARGS__), sym, ret, __VA_ARGS__)
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d880817..22fd9a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4492,6 +4492,8 @@
                                 + "snatched provider from the jaws of death");
                     }
                     prc.removePending = false;
+                    // There is a race! It fails to remove the message, which
+                    // will be handled in completeRemoveProvider().
                     mH.removeMessages(H.REMOVE_PROVIDER, prc);
                 } else {
                     unstableDelta = 0;
@@ -4671,6 +4673,11 @@
                 return;
             }
 
+            // More complicated race!! Some client managed to acquire the
+            // provider and release it before the removal was completed.
+            // Continue the removal, and abort the next remove message.
+            prc.removePending = false;
+
             final IBinder jBinder = prc.holder.provider.asBinder();
             ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
             if (existingPrc == prc) {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 32cc7fd..02401dc 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -398,6 +398,15 @@
     public String[] resourceDirs;
 
     /**
+     * String retrieved from the seinfo tag found in selinux policy. This value
+     * is useful in setting an SELinux security context on the process as well
+     * as its data directory.
+     *
+     * {@hide}
+     */
+    public String seinfo;
+
+    /**
      * Paths to all shared libraries this application is linked against.  This
      * field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
      * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving
@@ -477,6 +486,9 @@
         if (resourceDirs != null) {
             pw.println(prefix + "resourceDirs=" + resourceDirs);
         }
+        if (seinfo != null) {
+            pw.println(prefix + "seinfo=" + seinfo);
+        }
         pw.println(prefix + "dataDir=" + dataDir);
         if (sharedLibraryFiles != null) {
             pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles);
@@ -544,6 +556,7 @@
         publicSourceDir = orig.publicSourceDir;
         nativeLibraryDir = orig.nativeLibraryDir;
         resourceDirs = orig.resourceDirs;
+        seinfo = orig.seinfo;
         sharedLibraryFiles = orig.sharedLibraryFiles;
         dataDir = orig.dataDir;
         uid = orig.uid;
@@ -583,6 +596,7 @@
         dest.writeString(publicSourceDir);
         dest.writeString(nativeLibraryDir);
         dest.writeStringArray(resourceDirs);
+        dest.writeString(seinfo);
         dest.writeStringArray(sharedLibraryFiles);
         dest.writeString(dataDir);
         dest.writeInt(uid);
@@ -621,6 +635,7 @@
         publicSourceDir = source.readString();
         nativeLibraryDir = source.readString();
         resourceDirs = source.readStringArray();
+        seinfo = source.readString();
         sharedLibraryFiles = source.readStringArray();
         dataDir = source.readString();
         uid = source.readInt();
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index 651693a..a890d9b 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -148,6 +148,10 @@
                     for (int i = 0; i < size; i++) {
                         _result[i] = _reply.readString();
                     }
+                    int _ret = _reply.readInt();
+                    if (_ret != 1) {
+                        return null;
+                    }
                 } finally {
                     _reply.recycle();
                     _data.recycle();
@@ -401,6 +405,28 @@
                 }
                 return _result;
             }
+
+            @Override
+            public int duplicate(String srcKey, int srcUid, String destKey, int destUid)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(srcKey);
+                    _data.writeInt(srcUid);
+                    _data.writeString(destKey);
+                    _data.writeInt(destUid);
+                    mRemote.transact(Stub.TRANSACTION_duplicate, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
         }
 
         private static final String DESCRIPTOR = "android.security.keystore";
@@ -425,6 +451,7 @@
         static final int TRANSACTION_grant = IBinder.FIRST_CALL_TRANSACTION + 17;
         static final int TRANSACTION_ungrant = IBinder.FIRST_CALL_TRANSACTION + 18;
         static final int TRANSACTION_getmtime = IBinder.FIRST_CALL_TRANSACTION + 19;
+        static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20;
 
         /**
          * Cast an IBinder object into an IKeystoreService interface, generating
@@ -509,4 +536,7 @@
     public int ungrant(String name, int granteeUid) throws RemoteException;
 
     public long getmtime(String name) throws RemoteException;
+
+    public int duplicate(String srcKey, int srcUid, String destKey, int destUid)
+            throws RemoteException;
 }
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index ef2c248..50b1a29 100644
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -43,11 +43,17 @@
  * for both formatting and parsing dates. For the canonical documentation
  * of format strings, see {@link java.text.SimpleDateFormat}.
  *
- * <p>The format methods in this class implement a subset of Unicode
+ * <p>The {@code format} methods in this class implement a subset of Unicode
  * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a> patterns.
- * The subset supported by this class includes the following format characters:
- * {@code acdEHhLKkLMmsyz}. See {@link java.text.SimpleDateFormat} for more documentation
- * about patterns, or if you need a more compete implementation.
+ * The subset currently supported by this class includes the following format characters:
+ * {@code acdEHhLKkLMmsyz}. Up to API level 17, only {@code adEhkMmszy} were supported.
+ * Note that this class incorrectly implements {@code k} as if it were {@code H} for backwards
+ * compatibility.
+ *
+ * <p>See {@link java.text.SimpleDateFormat} for more documentation
+ * about patterns, or if you need a more complete or correct implementation.
+ * Note that the non-{@code format} methods in this class are implemented by
+ * {@code SimpleDateFormat}.
  */
 public class DateFormat {
     /** @deprecated Use a literal {@code '} instead. */
@@ -74,7 +80,11 @@
     @Deprecated
     public  static final char    HOUR                   =    'h';
 
-    /** @deprecated Use a literal {@code 'k'} instead. */
+    /**
+     * @deprecated Use a literal {@code 'H'} (for compatibility with {@link SimpleDateFormat}
+     * and Unicode) or {@code 'k'} (for compatibility with Android releases up to and including
+     * Jelly Bean MR-1) instead. Note that the two are incompatible.
+     */
     @Deprecated
     public  static final char    HOUR_OF_DAY            =    'k';
 
@@ -160,9 +170,18 @@
      * @return the {@link java.text.DateFormat} object that properly formats the time.
      */
     public static java.text.DateFormat getTimeFormat(Context context) {
+        return new java.text.SimpleDateFormat(getTimeFormatString(context));
+    }
+
+    /**
+     * Returns a String pattern that can be used to format the time according
+     * to the current locale and the user's 12-/24-hour clock preference.
+     * @param context the application context
+     * @hide
+     */
+    public static String getTimeFormatString(Context context) {
         LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
-        boolean is24 = is24HourFormat(context);
-        return new java.text.SimpleDateFormat(is24 ? d.timeFormat24 : d.timeFormat12);
+        return is24HourFormat(context) ? d.timeFormat24 : d.timeFormat12;
     }
 
     /**
@@ -441,10 +460,13 @@
                     }
                     break;
                 case 'H': // hour in day (0-23)
-                case 'k': // hour in day (1-24)
+                case 'k': // hour in day (1-24) [but see note below]
                     {
                         int hour = inDate.get(Calendar.HOUR_OF_DAY);
-                        if (c == 'k' && hour == 0) {
+                        // Historically on Android 'k' was interpreted as 'H', which wasn't
+                        // implemented, so pretty much all callers that want to format 24-hour
+                        // times are abusing 'k'. http://b/8359981.
+                        if (false && c == 'k' && hour == 0) {
                             hour = 24;
                         }
                         replacement = zeroPad(hour, count);
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 6c8a737..5a88cf6 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -39,7 +39,6 @@
 {
     private static final Object sLock = new Object();
     private static Configuration sLastConfig;
-    private static java.text.DateFormat sStatusTimeFormat;
     private static String sElapsedFormatMMSS;
     private static String sElapsedFormatHMMSS;
 
@@ -95,14 +94,14 @@
     // translated.
     /**
      * This is not actually the preferred 24-hour date format in all locales.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final String HOUR_MINUTE_24 = "%H:%M";
     public static final String MONTH_FORMAT = "%B";
     /**
      * This is not actually a useful month name in all locales.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final String ABBREV_MONTH_FORMAT = "%b";
@@ -118,7 +117,7 @@
     // The index is constructed from a bit-wise OR of the boolean values:
     // {showTime, showYear, showWeekDay}.  For example, if showYear and
     // showWeekDay are both true, then the index would be 3.
-    /** @deprecated do not use. */
+    /** @deprecated Do not use. */
     public static final int sameYearTable[] = {
         com.android.internal.R.string.same_year_md1_md2,
         com.android.internal.R.string.same_year_wday1_md1_wday2_md2,
@@ -145,7 +144,7 @@
     // The index is constructed from a bit-wise OR of the boolean values:
     // {showTime, showYear, showWeekDay}.  For example, if showYear and
     // showWeekDay are both true, then the index would be 3.
-    /** @deprecated do not use. */
+    /** @deprecated Do not use. */
     public static final int sameMonthTable[] = {
         com.android.internal.R.string.same_month_md1_md2,
         com.android.internal.R.string.same_month_wday1_md1_wday2_md2,
@@ -172,7 +171,7 @@
      *
      * @more <p>
      *       e.g. "Sunday" or "January"
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_LONG = 10;
@@ -183,7 +182,7 @@
      *
      * @more <p>
      *       e.g. "Sun" or "Jan"
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_MEDIUM = 20;
@@ -195,7 +194,7 @@
      * <p>e.g. "Su" or "Jan"
      * <p>In most languages, the results returned for LENGTH_SHORT will be the same as
      * the results returned for {@link #LENGTH_MEDIUM}.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_SHORT = 30;
@@ -204,7 +203,7 @@
      * Request an even shorter abbreviated version of the name.
      * Do not use this.  Currently this will always return the same result
      * as {@link #LENGTH_SHORT}.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_SHORTER = 40;
@@ -216,7 +215,7 @@
      * <p>e.g. "S", "T", "T" or "J"
      * <p>In some languages, the results returned for LENGTH_SHORTEST will be the same as
      * the results returned for {@link #LENGTH_SHORT}.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_SHORTEST = 50;
@@ -232,7 +231,7 @@
      *               Undefined lengths will return {@link #LENGTH_MEDIUM}
      *               but may return something different in the future.
      * @throws IndexOutOfBoundsException if the dayOfWeek is out of bounds.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
@@ -254,7 +253,7 @@
      * @param ampm Either {@link Calendar#AM Calendar.AM} or {@link Calendar#PM Calendar.PM}.
      * @throws IndexOutOfBoundsException if the ampm is out of bounds.
      * @return Localized version of "AM" or "PM".
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static String getAMPMString(int ampm) {
@@ -270,7 +269,7 @@
      *               Undefined lengths will return {@link #LENGTH_MEDIUM}
      *               but may return something different in the future.
      * @return Localized month of the year.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static String getMonthString(int month, int abbrev) {
@@ -288,36 +287,6 @@
     }
 
     /**
-     * Return a localized string for the month of the year, for
-     * contexts where the month is not formatted together with
-     * a day of the month.
-     *
-     * @param month One of {@link Calendar#JANUARY Calendar.JANUARY},
-     *               {@link Calendar#FEBRUARY Calendar.FEBRUARY}, etc.
-     * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_MEDIUM},
-     *               or {@link #LENGTH_SHORTEST}.
-     *               Undefined lengths will return {@link #LENGTH_MEDIUM}
-     *               but may return something different in the future.
-     * @return Localized month of the year.
-     * @hide Pending API council approval
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
-     */
-    @Deprecated
-    public static String getStandaloneMonthString(int month, int abbrev) {
-        LocaleData d = LocaleData.get(Locale.getDefault());
-        String[] names;
-        switch (abbrev) {
-            case LENGTH_LONG:       names = d.longStandAloneMonthNames; break;
-            case LENGTH_MEDIUM:     names = d.shortMonthNames; break;
-            case LENGTH_SHORT:      names = d.shortMonthNames; break;
-            case LENGTH_SHORTER:    names = d.shortMonthNames; break;
-            case LENGTH_SHORTEST:   names = d.tinyStandAloneMonthNames; break;
-            default:                names = d.shortMonthNames; break;
-        }
-        return names[month];
-    }
-
-    /**
      * Returns a string describing the elapsed time since startTime.
      * @param startTime some time in the past.
      * @return a String object containing the elapsed time.
@@ -544,25 +513,12 @@
         Configuration cfg = r.getConfiguration();
         if (sLastConfig == null || !sLastConfig.equals(cfg)) {
             sLastConfig = cfg;
-            sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT);
             sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);
             sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);
         }
     }
 
     /**
-     * Format a time so it appears like it would in the status bar clock.
-     * @deprecated use {@link #DateFormat.getTimeFormat(Context)} instead.
-     * @hide
-     */
-    public static final CharSequence timeString(long millis) {
-        synchronized (sLock) {
-            initFormatStringsLocked();
-            return sStatusTimeFormat.format(millis);
-        }
-    }
-
-    /**
      * Return given duration in a human-friendly format. For example, "4
      * minutes" or "1 second". Returns only largest meaningful unit of time,
      * from seconds up to hours.
@@ -676,18 +632,6 @@
     }
 
     /**
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static Calendar newCalendar(boolean zulu)
-    {
-        if (zulu)
-            return Calendar.getInstance(TimeZone.getTimeZone("GMT"));
-
-        return Calendar.getInstance();
-    }
-
-    /**
      * @return true if the supplied when is today else false
      */
     public static boolean isToday(long when) {
@@ -705,127 +649,6 @@
     }
 
     /**
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     * Return true if this date string is local time
-     */
-    public static boolean isUTC(String s)
-    {
-        if (s.length() == 16 && s.charAt(15) == 'Z') {
-            return true;
-        }
-        if (s.length() == 9 && s.charAt(8) == 'Z') {
-            // XXX not sure if this case possible/valid
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Return a string containing the date and time in RFC2445 format.
-     * Ensures that the time is written in UTC.  The Calendar class doesn't
-     * really help out with this, so this is slower than it ought to be.
-     *
-     * @param cal the date and time to write
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static String writeDateTime(Calendar cal)
-    {
-        TimeZone tz = TimeZone.getTimeZone("GMT");
-        GregorianCalendar c = new GregorianCalendar(tz);
-        c.setTimeInMillis(cal.getTimeInMillis());
-        return writeDateTime(c, true);
-    }
-
-    /**
-     * Return a string containing the date and time in RFC2445 format.
-     *
-     * @param cal the date and time to write
-     * @param zulu If the calendar is in UTC, pass true, and a Z will
-     * be written at the end as per RFC2445.  Otherwise, the time is
-     * considered in localtime.
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static String writeDateTime(Calendar cal, boolean zulu)
-    {
-        StringBuilder sb = new StringBuilder();
-        sb.ensureCapacity(16);
-        if (zulu) {
-            sb.setLength(16);
-            sb.setCharAt(15, 'Z');
-        } else {
-            sb.setLength(15);
-        }
-        return writeDateTime(cal, sb);
-    }
-
-    /**
-     * Return a string containing the date and time in RFC2445 format.
-     *
-     * @param cal the date and time to write
-     * @param sb a StringBuilder to use.  It is assumed that setLength
-     *           has already been called on sb to the appropriate length
-     *           which is sb.setLength(zulu ? 16 : 15)
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static String writeDateTime(Calendar cal, StringBuilder sb)
-    {
-        int n;
-
-        n = cal.get(Calendar.YEAR);
-        sb.setCharAt(3, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(2, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(1, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(0, (char)('0'+n%10));
-
-        n = cal.get(Calendar.MONTH) + 1;
-        sb.setCharAt(5, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(4, (char)('0'+n%10));
-
-        n = cal.get(Calendar.DAY_OF_MONTH);
-        sb.setCharAt(7, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(6, (char)('0'+n%10));
-
-        sb.setCharAt(8, 'T');
-
-        n = cal.get(Calendar.HOUR_OF_DAY);
-        sb.setCharAt(10, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(9, (char)('0'+n%10));
-
-        n = cal.get(Calendar.MINUTE);
-        sb.setCharAt(12, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(11, (char)('0'+n%10));
-
-        n = cal.get(Calendar.SECOND);
-        sb.setCharAt(14, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(13, (char)('0'+n%10));
-
-        return sb.toString();
-    }
-
-    /**
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static void assign(Calendar lval, Calendar rval)
-    {
-        // there should be a faster way.
-        lval.clear();
-        lval.setTimeInMillis(rval.getTimeInMillis());
-    }
-
-    /**
      * Formats a date or a time range according to the local conventions.
      * <p>
      * Note that this is a convenience method. Using it involves creating an
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 121c6f2..9c98b98 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -95,16 +95,12 @@
     }
 
     /**
-     * Returns a string in the canonical IP format ###.###.###.### from a packed integer containing
-     * the IP address.  The IP address is expected to be in little-endian format (LSB first). That
-     * is, 0x01020304 will return "4.3.2.1".
+     * Returns a string in the canonical IPv4 format ###.###.###.### from a packed integer
+     * containing the IP address. The IPv4 address is expected to be in little-endian
+     * format (LSB first). That is, 0x01020304 will return "4.3.2.1".
      *
-     * @param ipv4Address the IP address as a packed integer with LSB first.
-     * @return string with canonical IP address format.
-     *
-     * @deprecated this method doesn't support IPv6 addresses. Prefer {@link
-     *     java.net.InetAddress#getHostAddress()}, which supports both IPv4 and
-     *     IPv6 addresses.
+     * @deprecated Use {@link java.net.InetAddress#getHostAddress()}, which supports both IPv4 and
+     *     IPv6 addresses. This method does not support IPv6 addresses.
      */
     @Deprecated
     public static String formatIpAddress(int ipv4Address) {
diff --git a/core/java/android/widget/DigitalClock.java b/core/java/android/widget/DigitalClock.java
index c6b6dd6..b6c1e5b9 100644
--- a/core/java/android/widget/DigitalClock.java
+++ b/core/java/android/widget/DigitalClock.java
@@ -39,8 +39,6 @@
     // proportional fonts don't shake rendering
 
     Calendar mCalendar;
-    private final static String m12 = "h:mm:ss aa";
-    private final static String m24 = "k:mm:ss";
     @SuppressWarnings("FieldCanBeLocal") // We must keep a reference to this observer
     private FormatChangeObserver mFormatChangeObserver;
 
@@ -102,19 +100,8 @@
         mTickerStopped = true;
     }
 
-    /**
-     * Pulls 12/24 mode from system settings
-     */
-    private boolean get24HourMode() {
-        return android.text.format.DateFormat.is24HourFormat(getContext());
-    }
-
     private void setFormat() {
-        if (get24HourMode()) {
-            mFormat = m24;
-        } else {
-            mFormat = m12;
-        }
+        mFormat = DateFormat.getTimeFormatString(getContext());
     }
 
     private class FormatChangeObserver extends ContentObserver {
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 290d9b5..2f08253 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -36,21 +36,24 @@
 import java.util.Calendar;
 import java.util.TimeZone;
 
+import libcore.icu.LocaleData;
+
 import static android.view.ViewDebug.ExportedProperty;
 import static android.widget.RemoteViews.*;
 
 /**
  * <p><code>TextClock</code> can display the current date and/or time as
  * a formatted string.</p>
- * 
+ *
  * <p>This view honors the 24-hour format system setting. As such, it is
  * possible and recommended to provide two different formatting patterns:
  * one to display the date/time in 24-hour mode and one to display the
- * date/time in 12-hour mode.</p>
- * 
+ * date/time in 12-hour mode. Most callers will want to use the defaults,
+ * though, which will be appropriate for the user's locale.</p>
+ *
  * <p>It is possible to determine whether the system is currently in
  * 24-hour mode by calling {@link #is24HourModeEnabled()}.</p>
- * 
+ *
  * <p>The rules used by this widget to decide how to format the date and
  * time are the following:</p>
  * <ul>
@@ -58,22 +61,24 @@
  *         <ul>
  *             <li>Use the value returned by {@link #getFormat24Hour()} when non-null</li>
  *             <li>Otherwise, use the value returned by {@link #getFormat12Hour()} when non-null</li>
- *             <li>Otherwise, use {@link #DEFAULT_FORMAT_24_HOUR}</li>
+ *             <li>Otherwise, use a default value appropriate for the user's locale, such as {@code h:mm a}</li>
  *         </ul>
  *     </li>
  *     <li>In 12-hour mode:
  *         <ul>
  *             <li>Use the value returned by {@link #getFormat12Hour()} when non-null</li>
  *             <li>Otherwise, use the value returned by {@link #getFormat24Hour()} when non-null</li>
- *             <li>Otherwise, use {@link #DEFAULT_FORMAT_12_HOUR}</li>
+ *             <li>Otherwise, use a default value appropriate for the user's locale, such as {@code HH:mm}</li>
  *         </ul>
  *     </li>
  * </ul>
- * 
+ *
  * <p>The {@link CharSequence} instances used as formatting patterns when calling either
  * {@link #setFormat24Hour(CharSequence)} or {@link #setFormat12Hour(CharSequence)} can
- * contain styling information. To do so, use a {@link android.text.Spanned} object.</p>
- * 
+ * contain styling information. To do so, use a {@link android.text.Spanned} object.
+ * Note that if you customize these strings, it is your responsibility to supply strings
+ * appropriate for formatting dates and/or times in the user's locale.</p>
+ *
  * @attr ref android.R.styleable#TextClock_format12Hour
  * @attr ref android.R.styleable#TextClock_format24Hour
  * @attr ref android.R.styleable#TextClock_timeZone
@@ -81,32 +86,34 @@
 @RemoteView
 public class TextClock extends TextView {
     /**
-     * The default formatting pattern in 12-hour mode. This pattenr is used
+     * The default formatting pattern in 12-hour mode. This pattern is used
      * if {@link #setFormat12Hour(CharSequence)} is called with a null pattern
      * or if no pattern was specified when creating an instance of this class.
-     * 
+     *
      * This default pattern shows only the time, hours and minutes, and an am/pm
      * indicator.
      *
      * @see #setFormat12Hour(CharSequence)
      * @see #getFormat12Hour()
+     * @deprecated Let the system use locale-appropriate defaults instead.
      */
-    public static final CharSequence DEFAULT_FORMAT_12_HOUR = "h:mm aa";
+    public static final CharSequence DEFAULT_FORMAT_12_HOUR = "h:mm a";
 
     /**
-     * The default formatting pattern in 24-hour mode. This pattenr is used
+     * The default formatting pattern in 24-hour mode. This pattern is used
      * if {@link #setFormat24Hour(CharSequence)} is called with a null pattern
      * or if no pattern was specified when creating an instance of this class.
      *
      * This default pattern shows only the time, hours and minutes.
-     * 
-     * @see #setFormat24Hour(CharSequence) 
-     * @see #getFormat24Hour() 
+     *
+     * @see #setFormat24Hour(CharSequence)
+     * @see #getFormat24Hour()
+     * @deprecated Let the system use locale-appropriate defaults instead.
      */
-    public static final CharSequence DEFAULT_FORMAT_24_HOUR = "k:mm";
+    public static final CharSequence DEFAULT_FORMAT_24_HOUR = "H:mm";
 
-    private CharSequence mFormat12 = DEFAULT_FORMAT_12_HOUR;
-    private CharSequence mFormat24 = DEFAULT_FORMAT_24_HOUR;
+    private CharSequence mFormat12;
+    private CharSequence mFormat24;
 
     @ExportedProperty
     private CharSequence mFormat;
@@ -158,7 +165,7 @@
      * Creates a new clock using the default patterns
      * {@link #DEFAULT_FORMAT_24_HOUR} and {@link #DEFAULT_FORMAT_12_HOUR}
      * respectively for the 24-hour and 12-hour modes.
-     * 
+     *
      * @param context The Context the view is running in, through which it can
      *        access the current theme, resources, etc.
      */
@@ -171,7 +178,7 @@
     /**
      * Creates a new clock inflated from XML. This object's properties are
      * intialized from the attributes specified in XML.
-     * 
+     *
      * This constructor uses a default style of 0, so the only attribute values
      * applied are those in the Context's Theme and the given AttributeSet.
      *
@@ -201,14 +208,8 @@
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TextClock, defStyle, 0);
         try {
-            CharSequence format;
-
-            format = a.getText(R.styleable.TextClock_format12Hour);
-            mFormat12 = format == null ? DEFAULT_FORMAT_12_HOUR : format;
-
-            format = a.getText(R.styleable.TextClock_format24Hour);
-            mFormat24 = format == null ? DEFAULT_FORMAT_24_HOUR : format;
-
+            mFormat12 = a.getText(R.styleable.TextClock_format12Hour);
+            mFormat24 = a.getText(R.styleable.TextClock_format24Hour);
             mTimeZone = a.getString(R.styleable.TextClock_timeZone);
         } finally {
             a.recycle();
@@ -218,6 +219,16 @@
     }
 
     private void init() {
+        if (mFormat12 == null || mFormat24 == null) {
+            LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
+            if (mFormat12 == null) {
+                mFormat12 = ld.timeFormat12;
+            }
+            if (mFormat24 == null) {
+                mFormat24 = ld.timeFormat24;
+            }
+        }
+
         createTime(mTimeZone);
         // Wait until onAttachedToWindow() to handle the ticker
         chooseFormat(false);
@@ -235,11 +246,11 @@
      * Returns the formatting pattern used to display the date and/or time
      * in 12-hour mode. The formatting pattern syntax is described in
      * {@link DateFormat}.
-     * 
+     *
      * @return A {@link CharSequence} or null.
-     * 
-     * @see #setFormat12Hour(CharSequence) 
-     * @see #is24HourModeEnabled() 
+     *
+     * @see #setFormat12Hour(CharSequence)
+     * @see #is24HourModeEnabled()
      */
     @ExportedProperty
     public CharSequence getFormat12Hour() {
@@ -257,12 +268,12 @@
      * {@link #DEFAULT_FORMAT_12_HOUR} will be used instead.
      *
      * @param format A date/time formatting pattern as described in {@link DateFormat}
-     * 
+     *
      * @see #getFormat12Hour()
      * @see #is24HourModeEnabled()
      * @see #DEFAULT_FORMAT_12_HOUR
      * @see DateFormat
-     * 
+     *
      * @attr ref android.R.styleable#TextClock_format12Hour
      */
     @RemotableViewMethod
@@ -292,7 +303,7 @@
      * Specifies the formatting pattern used to display the date and/or time
      * in 24-hour mode. The formatting pattern syntax is described in
      * {@link DateFormat}.
-     * 
+     *
      * If this pattern is set to null, {@link #getFormat12Hour()} will be used
      * even in 24-hour mode. If both 24-hour and 12-hour formatting patterns
      * are set to null, {@link #DEFAULT_FORMAT_24_HOUR} and
@@ -301,7 +312,7 @@
      * @param format A date/time formatting pattern as described in {@link DateFormat}
      *
      * @see #getFormat24Hour()
-     * @see #is24HourModeEnabled() 
+     * @see #is24HourModeEnabled()
      * @see #DEFAULT_FORMAT_24_HOUR
      * @see DateFormat
      *
@@ -317,22 +328,22 @@
 
     /**
      * Indicates whether the system is currently using the 24-hour mode.
-     * 
+     *
      * When the system is in 24-hour mode, this view will use the pattern
      * returned by {@link #getFormat24Hour()}. In 12-hour mode, the pattern
      * returned by {@link #getFormat12Hour()} is used instead.
-     * 
+     *
      * If either one of the formats is null, the other format is used. If
      * both formats are null, the default values {@link #DEFAULT_FORMAT_12_HOUR}
      * and {@link #DEFAULT_FORMAT_24_HOUR} are used instead.
-     * 
+     *
      * @return true if time should be displayed in 24-hour format, false if it
      *         should be displayed in 12-hour format.
-     * 
+     *
      * @see #setFormat12Hour(CharSequence)
-     * @see #getFormat12Hour() 
+     * @see #getFormat12Hour()
      * @see #setFormat24Hour(CharSequence)
-     * @see #getFormat24Hour() 
+     * @see #getFormat24Hour()
      */
     public boolean is24HourModeEnabled() {
         return DateFormat.is24HourFormat(getContext());
@@ -340,13 +351,13 @@
 
     /**
      * Indicates which time zone is currently used by this view.
-     * 
+     *
      * @return The ID of the current time zone or null if the default time zone,
      *         as set by the user, must be used
      *
      * @see TimeZone
      * @see java.util.TimeZone#getAvailableIDs()
-     * @see #setTimeZone(String) 
+     * @see #setTimeZone(String)
      */
     public String getTimeZone() {
         return mTimeZone;
@@ -378,7 +389,7 @@
     /**
      * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
      * depending on whether the user has selected 24-hour format.
-     * 
+     *
      * Calling this method does not schedule or unschedule the time ticker.
      */
     private void chooseFormat() {
@@ -388,17 +399,19 @@
     /**
      * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
      * depending on whether the user has selected 24-hour format.
-     * 
+     *
      * @param handleTicker true if calling this method should schedule/unschedule the
      *                     time ticker, false otherwise
      */
     private void chooseFormat(boolean handleTicker) {
         final boolean format24Requested = is24HourModeEnabled();
 
+        LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
+
         if (format24Requested) {
-            mFormat = abc(mFormat24, mFormat12, DEFAULT_FORMAT_24_HOUR);
+            mFormat = abc(mFormat24, mFormat12, ld.timeFormat24);
         } else {
-            mFormat = abc(mFormat12, mFormat24, DEFAULT_FORMAT_12_HOUR);
+            mFormat = abc(mFormat12, mFormat24, ld.timeFormat12);
         }
 
         boolean hadSeconds = mHasSeconds;
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index eed3e67..eb2d1fe 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -27,6 +27,7 @@
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.util.Log;
 
 import com.android.org.bouncycastle.util.encoders.Base64;
@@ -64,6 +65,10 @@
 
     public LocalTransport(Context context) {
         mContext = context;
+        mDataDir.mkdirs();
+        if (!SELinux.restorecon(mDataDir)) {
+            Log.e(TAG, "SELinux restorecon failed for " + mDataDir);
+        }
     }
 
     public Intent configurationIntent() {
diff --git a/core/jni/android_ddm_DdmHandleNativeHeap.cpp b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
index 42d408d..f5eaf94 100644
--- a/core/jni/android_ddm_DdmHandleNativeHeap.cpp
+++ b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
@@ -2,16 +2,16 @@
 **
 ** Copyright 2006, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** 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 
+**     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 
+** 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.
 */
 
@@ -23,20 +23,17 @@
 #include <android_runtime/AndroidRuntime.h>
 
 #include <utils/Log.h>
+#include <utils/String8.h>
 
 #include <fcntl.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#if defined(__arm__)
-extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, 
-        size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
-        
-extern "C" void free_malloc_leak_info(uint8_t* info);
-#endif
+extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
+                                     size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
 
-#define MAPS_FILE_SIZE 65 * 1024
+extern "C" void free_malloc_leak_info(uint8_t* info);
 
 struct Header {
     size_t mapSize;
@@ -48,96 +45,57 @@
 
 namespace android {
 
+static void ReadFile(const char* path, String8& s) {
+    int fd = open(path, O_RDONLY);
+    if (fd != -1) {
+        char bytes[1024];
+        ssize_t byteCount;
+        while ((byteCount = TEMP_FAILURE_RETRY(read(fd, bytes, sizeof(bytes)))) > 0) {
+            s.append(bytes, byteCount);
+        }
+        close(fd);
+    }
+}
+
 /*
- * Retrieve the native heap information and the info from /proc/<self>/maps,
+ * Retrieve the native heap information and the info from /proc/self/maps,
  * copy them into a byte[] with a "struct Header" that holds data offsets,
  * and return the array.
  */
-static jbyteArray getLeakInfo(JNIEnv *env, jobject clazz)
-{
-#if defined(__arm__)
-    // get the info in /proc/[pid]/map
+static jbyteArray DdmHandleNativeHeap_getLeakInfo(JNIEnv* env, jobject) {
     Header header;
     memset(&header, 0, sizeof(header));
 
-    pid_t pid = getpid();
-
-    char path[FILENAME_MAX];
-    sprintf(path, "/proc/%d/maps", pid);
-
-    struct stat sb;
-    int ret = stat(path, &sb);
-
-    uint8_t* mapsFile = NULL;
-    if (ret == 0) {
-        mapsFile = (uint8_t*)malloc(MAPS_FILE_SIZE);
-        int fd = open(path, O_RDONLY);
-    
-        if (mapsFile != NULL && fd != -1) {
-            int amount = 0;
-            do {
-                uint8_t* ptr = mapsFile + header.mapSize;
-                amount = read(fd, ptr, MAPS_FILE_SIZE);
-                if (amount <= 0) {
-                    if (errno != EINTR)
-                        break; 
-                    else
-                        continue;
-                }
-                header.mapSize += amount;
-            } while (header.mapSize < MAPS_FILE_SIZE);
-            
-            ALOGD("**** read %d bytes from '%s'", (int) header.mapSize, path);
-        }
-    }
+    String8 maps;
+    ReadFile("/proc/self/maps", maps);
+    header.mapSize = maps.size();
 
     uint8_t* allocBytes;
-    get_malloc_leak_info(&allocBytes, &header.allocSize, &header.allocInfoSize, 
-            &header.totalMemory, &header.backtraceSize);
+    get_malloc_leak_info(&allocBytes, &header.allocSize, &header.allocInfoSize,
+                         &header.totalMemory, &header.backtraceSize);
 
-    jbyte* bytes = NULL;
-    jbyte* ptr = NULL;
+    ALOGD("*** mapSize: %d allocSize: %d allocInfoSize: %d totalMemory: %d",
+          header.mapSize, header.allocSize, header.allocInfoSize, header.totalMemory);
+
     jbyteArray array = env->NewByteArray(sizeof(Header) + header.mapSize + header.allocSize);
-    if (array == NULL) {
-        goto done;
+    if (array != NULL) {
+        env->SetByteArrayRegion(array, 0,
+                                sizeof(header), reinterpret_cast<jbyte*>(&header));
+        env->SetByteArrayRegion(array, sizeof(header),
+                                maps.size(), reinterpret_cast<const jbyte*>(maps.string()));
+        env->SetByteArrayRegion(array, sizeof(header) + maps.size(),
+                                header.allocSize, reinterpret_cast<jbyte*>(allocBytes));
     }
 
-    bytes = env->GetByteArrayElements(array, NULL);
-    ptr = bytes;
-
-//    ALOGD("*** mapSize: %d allocSize: %d allocInfoSize: %d totalMemory: %d", 
-//            header.mapSize, header.allocSize, header.allocInfoSize, header.totalMemory);
-
-    memcpy(ptr, &header, sizeof(header));
-    ptr += sizeof(header);
-    
-    if (header.mapSize > 0 && mapsFile != NULL) {
-        memcpy(ptr, mapsFile, header.mapSize);
-        ptr += header.mapSize;
-    }
-    
-    memcpy(ptr, allocBytes, header.allocSize);
-    env->ReleaseByteArrayElements(array, bytes, 0);
-
-done:
-    if (mapsFile != NULL) {
-        free(mapsFile);
-    }
-    // free the info up!
     free_malloc_leak_info(allocBytes);
-
     return array;
-#else
-    return NULL;
-#endif
 }
 
 static JNINativeMethod method_table[] = {
-    { "getLeakInfo", "()[B", (void*)getLeakInfo },
+    { "getLeakInfo", "()[B", (void*) DdmHandleNativeHeap_getLeakInfo },
 };
 
-int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env)
-{
+int register_android_ddm_DdmHandleNativeHeap(JNIEnv* env) {
     return AndroidRuntime::registerNativeMethods(env, "android/ddm/DdmHandleNativeHeap", method_table, NELEM(method_table));
 }
 
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index b12fdfc..0a97f39 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -23,428 +23,407 @@
 #include "selinux/selinux.h"
 #include "selinux/android.h"
 #include <errno.h>
+#include <ScopedLocalRef.h>
+#include <ScopedUtfChars.h>
+#include <UniquePtr.h>
 
 namespace android {
 
-  static jboolean isSELinuxDisabled = true;
+struct SecurityContext_Delete {
+    void operator()(security_context_t p) const {
+        freecon(p);
+    }
+};
+typedef UniquePtr<char[], SecurityContext_Delete> Unique_SecurityContext;
 
-  static void throw_NullPointerException(JNIEnv *env, const char* msg) {
-    jclass clazz;
-    clazz = env->FindClass("java/lang/NullPointerException");
-    env->ThrowNew(clazz, msg);
-  }
+static jboolean isSELinuxDisabled = true;
 
-  /*
-   * 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) {
-
+/*
+ * 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) {
     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) {
+/*
+ * 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) {
     return (security_getenforce() == 1) ? true : false;
-  }
+}
 
-  /*
-   * 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) {
-    if (isSELinuxDisabled)
-      return false;
+/*
+ * 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, jboolean value) {
+    if (isSELinuxDisabled) {
+        return false;
+    }
 
-    int enforce = (value) ? 1 : 0;
+    int enforce = value ? 1 : 0;
 
     return (security_setenforce(enforce) != -1) ? true : false;
-  }
+}
 
-  /*
-   * 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) {
-    if (isSELinuxDisabled)
-      return NULL;
+/*
+ * 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, jobject fileDescriptor) {
+    if (isSELinuxDisabled) {
+        return NULL;
+    }
 
     if (fileDescriptor == NULL) {
-      throw_NullPointerException(env, "Trying to check security context of a null peer socket.");
-      return NULL;
+        jniThrowNullPointerException(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) {
-      ALOGE("There was an issue with retrieving the file descriptor");
-      goto bail;
+        ALOGE("getPeerCon => getFD for %p failed", fileDescriptor);
+        return NULL;
     }
 
-    if (getpeercon(fd, &context) == -1)
-      goto bail;
+    security_context_t tmp;
+    int ret = getpeercon(fd, &tmp);
+    Unique_SecurityContext context(tmp);
 
-    ALOGV("getPeerCon: Successfully retrived context of peer socket '%s'", context);
-
-    securityString = env->NewStringUTF(context);
-
-  bail:
-    if (context != NULL)
-      freecon(context);
-
-    return securityString;
-  }
-
-  /*
-   * 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) {
-    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);
+    ScopedLocalRef<jstring> contextStr(env, NULL);
+    if (ret != -1) {
+        contextStr.reset(env->NewStringUTF(context.get()));
     }
 
-    int ret;
-    if ((ret = setfscreatecon(securityContext)) == -1)
-      goto bail;
+    ALOGV("getPeerCon(%d) => %s", fd, contextStr.get());
+    return contextStr.release();
+}
 
-    ALOGV("setFSCreateCon: set new security context to '%s' ", context == NULL ? "default", context);
+/*
+ * 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, jstring contextStr) {
+    if (isSELinuxDisabled) {
+        return false;
+    }
 
-  bail:
-    if (constant_securityContext != NULL)
-      env->ReleaseStringUTFChars(context, constant_securityContext);
+    UniquePtr<ScopedUtfChars> context;
+    const char* context_c_str = NULL;
+    if (contextStr != NULL) {
+        context.reset(new ScopedUtfChars(env, contextStr));
+        context_c_str = context->c_str();
+        if (context_c_str == NULL) {
+            return false;
+        }
+    }
+
+    int ret = setfscreatecon(const_cast<char *>(context_c_str));
+
+    ALOGV("setFSCreateCon(%s) => %d", context_c_str, ret);
 
     return (ret == 0) ? true : false;
-  }
+}
 
-  /*
-   * 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) {
-    if (isSELinuxDisabled)
-      return false;
-
-    if (path == NULL) {
-      throw_NullPointerException(env, "Trying to change the security context of a NULL file object.");
-      return false;
+/*
+ * Function: setFileCon
+ * Purpose:  set the security context of a file object
+ * Parameters:
+ *       path: the location of the file system object
+ *       context: 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, jstring pathStr, jstring contextStr) {
+    if (isSELinuxDisabled) {
+        return false;
     }
 
-    if (con == NULL) {
-      throw_NullPointerException(env, "Trying to set the security context of a file object with NULL.");
-      return false;
+    ScopedUtfChars path(env, pathStr);
+    if (path.c_str() == NULL) {
+        return false;
     }
 
-    const char *objectPath = env->GetStringUTFChars(path, NULL);
-    const char *constant_con = env->GetStringUTFChars(con, NULL);
+    ScopedUtfChars context(env, contextStr);
+    if (context.c_str() == NULL) {
+        return false;
+    }
 
     // GetStringUTFChars returns const char * yet setfilecon needs char *
-    char *newCon = const_cast<char *>(constant_con);
+    char *tmp = const_cast<char *>(context.c_str());
+    int ret = setfilecon(path.c_str(), tmp);
 
-    int ret;
-    if ((ret = setfilecon(objectPath, newCon)) == -1)
-      goto bail;
-
-    ALOGV("setFileCon: Succesfully set security context '%s' for '%s'", newCon, objectPath);
-
-  bail:
-    env->ReleaseStringUTFChars(path, objectPath);
-    env->ReleaseStringUTFChars(con, constant_con);
+    ALOGV("setFileCon(%s, %s) => %d", path.c_str(), context.c_str(), ret);
     return (ret == 0) ? true : false;
-  }
+}
 
-  /*
-   * 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) {
-    if (isSELinuxDisabled)
-      return NULL;
-
-    if (path == NULL) {
-      throw_NullPointerException(env, "Trying to check security context of a null path.");
-      return NULL;
+/*
+ * 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, jstring pathStr) {
+    if (isSELinuxDisabled) {
+        return NULL;
     }
 
-    const char *objectPath = env->GetStringUTFChars(path, NULL);
+    ScopedUtfChars path(env, pathStr);
+    if (path.c_str() == NULL) {
+        return NULL;
+    }
 
-    security_context_t context = NULL;
-    jstring securityString = NULL;
+    security_context_t tmp;
+    int ret = getfilecon(path.c_str(), &tmp);
+    Unique_SecurityContext context(tmp);
 
-    if (getfilecon(objectPath, &context) == -1)
-      goto bail;
+    ScopedLocalRef<jstring> securityString(env, NULL);
+    if (ret != -1) {
+        securityString.reset(env->NewStringUTF(context.get()));
+    }
 
-    ALOGV("getFileCon: Successfully retrived context '%s' for file '%s'", context, objectPath);
+    ALOGV("getFileCon(%s) => %s", path.c_str(), context.get());
+    return securityString.release();
+}
 
-    securityString = env->NewStringUTF(context);
+/*
+ * 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) {
+    if (isSELinuxDisabled) {
+        return NULL;
+    }
 
-  bail:
-    if (context != NULL)
-      freecon(context);
+    security_context_t tmp;
+    int ret = getcon(&tmp);
+    Unique_SecurityContext context(tmp);
 
-    env->ReleaseStringUTFChars(path, objectPath);
+    ScopedLocalRef<jstring> securityString(env, NULL);
+    if (ret != -1) {
+        securityString.reset(env->NewStringUTF(context.get()));
+    }
 
-    return securityString;
-  }
+    ALOGV("getCon() => %s", context.get());
+    return securityString.release();
+}
 
-  /*
-   * 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) {
-    if (isSELinuxDisabled)
-      return NULL;
+/*
+ * 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, jint pid) {
+    if (isSELinuxDisabled) {
+        return NULL;
+    }
 
-    security_context_t context = NULL;
-    jstring securityString = NULL;
+    security_context_t tmp;
+    int ret = getpidcon(static_cast<pid_t>(pid), &tmp);
+    Unique_SecurityContext context(tmp);
 
-    if (getcon(&context) == -1)
-      goto bail;
+    ScopedLocalRef<jstring> securityString(env, NULL);
+    if (ret != -1) {
+        securityString.reset(env->NewStringUTF(context.get()));
+    }
 
-    ALOGV("getCon: Successfully retrieved context '%s'", context);
+    ALOGV("getPidCon(%d) => %s", pid, context.get());
+    return securityString.release();
+}
 
-    securityString = env->NewStringUTF(context);
-
-  bail:
-    if (context != NULL)
-      freecon(context);
-
-    return securityString;
-  }
-
-  /*
-   * 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) {
-    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;
-
-    ALOGV("getPidCon: Successfully retrived context '%s' for pid '%d'", context, checkPid);
-
-    securityString = env->NewStringUTF(context);
-
-  bail:
-    if (context != NULL)
-      freecon(context);
-
-    return securityString;
-  }
-
-  /*
-   * 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) {
-    if (isSELinuxDisabled)
-      return NULL;
+/*
+ * 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) {
+    if (isSELinuxDisabled) {
+        return NULL;
+    }
 
     char **list;
-    int i, len, ret;
-    jclass stringClass;
-    jobjectArray stringArray = NULL;
+    int len;
+    if (security_get_boolean_names(&list, &len) == -1) {
+        return 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]);
+    jclass stringClass = env->FindClass("java/lang/String");
+    jobjectArray stringArray = env->NewObjectArray(len, stringClass, NULL);
+    for (int i = 0; i < len; i++) {
+        ScopedLocalRef<jstring> obj(env, env->NewStringUTF(list[i]));
+        env->SetObjectArrayElement(stringArray, i, obj.get());
+        free(list[i]);
     }
     free(list);
 
     return stringArray;
-  }
+}
 
-  /*
-   * 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) {
-    if (isSELinuxDisabled)
-      return false;
+/*
+ * 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, jstring nameStr) {
+    if (isSELinuxDisabled) {
+        return false;
+    }
 
-    const char *boolean_name;
-    int ret;
+    if (nameStr == NULL) {
+        return false;
+    }
 
-    if (name == NULL)
-      return false;
-    boolean_name = env->GetStringUTFChars(name, NULL);
-    ret = security_get_boolean_active(boolean_name);
-    env->ReleaseStringUTFChars(name, boolean_name);
+    ScopedUtfChars name(env, nameStr);
+    int ret = security_get_boolean_active(name.c_str());
+
+    ALOGV("getBooleanValue(%s) => %d", name.c_str(), ret);
     return (ret == 1) ? true : false;
-  }
+}
 
-  /*
-   * 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) {
-    if (isSELinuxDisabled)
-      return false;
+/*
+ * 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, jstring nameStr, jboolean value) {
+    if (isSELinuxDisabled) {
+        return false;
+    }
 
-    const char *boolean_name = NULL;
-    int ret;
+    if (nameStr == NULL) {
+        return false;
+    }
 
-    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;
+    ScopedUtfChars name(env, nameStr);
+    int ret = security_set_boolean(name.c_str(), value ? 1 : 0);
+    if (ret) {
+        return false;
+    }
 
-    if (security_commit_booleans() == -1)
-      return false;
+    if (security_commit_booleans() == -1) {
+        return false;
+    }
 
     return true;
-  }
+}
 
-  /*
-   * 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) {
-    if (isSELinuxDisabled)
-      return true;
+/*
+ * Function: checkSELinuxAccess
+ * Purpose: Check permissions between two security contexts.
+ * Parameters: subjectContextStr: subject security context as a string
+ *             objectContextStr: object security context as a string
+ *             objectClassStr: object's security class name as a string
+ *             permissionStr: permission name as a string
+ * Returns: boolean: (true) if permission was granted, (false) otherwise
+ * Exceptions: None
+ */
+static jboolean checkSELinuxAccess(JNIEnv *env, jobject, jstring subjectContextStr,
+        jstring objectContextStr, jstring objectClassStr, jstring permissionStr) {
+    if (isSELinuxDisabled) {
+        return true;
+    }
 
-    int accessGranted = -1;
+    ScopedUtfChars subjectContext(env, subjectContextStr);
+    if (subjectContext.c_str() == NULL) {
+        return false;
+    }
 
-    const char *const_scon, *const_tcon, *mytclass, *myperm;
-    char *myscon, *mytcon;
+    ScopedUtfChars objectContext(env, objectContextStr);
+    if (objectContext.c_str() == NULL) {
+        return false;
+    }
 
-    if (scon == NULL || tcon == NULL || tclass == NULL || perm == NULL)
-      goto bail;
+    ScopedUtfChars objectClass(env, objectClassStr);
+    if (objectClass.c_str() == NULL) {
+        return false;
+    }
 
-    const_scon = env->GetStringUTFChars(scon, NULL);
-    const_tcon = env->GetStringUTFChars(tcon, NULL);
-    mytclass   = env->GetStringUTFChars(tclass, NULL);
-    myperm     = env->GetStringUTFChars(perm, NULL);
+    ScopedUtfChars permission(env, permissionStr);
+    if (permission.c_str() == NULL) {
+        return false;
+    }
 
-    // selinux_check_access needs char* for some
-    myscon = const_cast<char *>(const_scon);
-    mytcon = const_cast<char *>(const_tcon);
+    char *tmp1 = const_cast<char *>(subjectContext.c_str());
+    char *tmp2 = const_cast<char *>(objectContext.c_str());
+    int accessGranted = selinux_check_access(tmp1, tmp2, objectClass.c_str(), permission.c_str(),
+            NULL);
 
-    accessGranted = selinux_check_access(myscon, mytcon, mytclass, myperm, NULL);
+    ALOGV("checkSELinuxAccess(%s, %s, %s, %s) => %d", subjectContext.c_str(), objectContext.c_str(),
+            objectClass.c_str(), permission.c_str(), accessGranted);
 
-    ALOGV("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;
-  }
+}
 
-  /*
-   * Function: native_restorecon
-   * Purpose: restore default SELinux security context
-   * Parameters: pathname: the pathname for the file to be relabeled
-   * Returns: boolean: (true) file label successfully restored, (false) otherwise
-   * Exceptions: none
-   */
-  static jboolean native_restorecon(JNIEnv *env, jobject clazz, jstring pathname) {
-    if (isSELinuxDisabled)
-      return true;
+/*
+ * Function: native_restorecon
+ * Purpose: restore default SELinux security context
+ * Parameters: pathname: the pathname for the file to be relabeled
+ * Returns: boolean: (true) file label successfully restored, (false) otherwise
+ * Exceptions: none
+ */
+static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr) {
+    if (isSELinuxDisabled) {
+        return true;
+    }
 
-    const char *file = const_cast<char *>(env->GetStringUTFChars(pathname, NULL));
-    int ret = selinux_android_restorecon(file);
-    env->ReleaseStringUTFChars(pathname, file);
+    ScopedUtfChars pathname(env, pathnameStr);
+    if (pathname.c_str() == NULL) {
+        ALOGV("restorecon(%p) => threw exception", pathname);
+        return false;
+    }
+
+    int ret = selinux_android_restorecon(pathname.c_str());
+    ALOGV("restorecon(%s) => %d", pathname.c_str(), ret);
     return (ret == 0);
-  }
+}
 
-  /*
-   * JNI registration.
-   */
-  static JNINativeMethod method_table[] = {
-
+/*
+ * 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  },
@@ -460,25 +439,25 @@
     { "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, ...) {
+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) {
+int register_android_os_SELinux(JNIEnv *env) {
     union selinux_callback cb;
     cb.func_log = log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
     isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false;
 
-    return AndroidRuntime::registerNativeMethods(
-         env, "android/os/SELinux",
-         method_table, NELEM(method_table));
-  }
+    return AndroidRuntime::registerNativeMethods(env, "android/os/SELinux", method_table,
+            NELEM(method_table));
+}
+
 }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8e66a77..cc6131c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3050,12 +3050,12 @@
         <!-- Specifies the formatting pattern used to show the time and/or date
              in 12-hour mode. Please refer to {@link android.text.format.DateFormat}
              for a complete description of accepted formatting patterns.
-             The default pattern is "h:mm aa". -->
+             The default pattern is a locale-appropriate equivalent of "h:mm a". -->
         <attr name="format12Hour" format="string"/>
         <!-- Specifies the formatting pattern used to show the time and/or date
              in 24-hour mode. Please refer to {@link android.text.format.DateFormat}
              for a complete description of accepted formatting patterns.
-             The default pattern is "k:mm". -->
+             The default pattern is a locale-appropriate equivalent of "H:mm". -->
         <attr name="format24Hour" format="string"/>
         <!-- Specifies the time zone to use. When this attribute is specified, the
              TextClock will ignore the time zone of the system. To use the user's
diff --git a/core/tests/coretests/src/android/webkit/WebkitTest.java b/core/tests/coretests/src/android/webkit/WebkitTest.java
index 17b4088..4685e3c 100644
--- a/core/tests/coretests/src/android/webkit/WebkitTest.java
+++ b/core/tests/coretests/src/android/webkit/WebkitTest.java
@@ -52,7 +52,7 @@
             date.setTime(time);
             c.setTime(date);
             index = dateSorter.getIndex(time);
-            Log.i(LOGTAG, "time: " + DateFormat.format("yyyy/MM/dd kk:mm:ss", c).toString() +
+            Log.i(LOGTAG, "time: " + DateFormat.format("yyyy/MM/dd HH:mm:ss", c).toString() +
                     " " + index + " " + dateSorter.getLabel(index));
         }
     }
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 65d7b8f..8a9826b 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -453,17 +453,19 @@
          * convention.
          */
         final String[] certAliases = mKeyStore.saw(Credentials.USER_CERTIFICATE);
-        for (String alias : certAliases) {
-            final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
-            if (certBytes == null) {
-                continue;
-            }
+        if (certAliases != null) {
+            for (String alias : certAliases) {
+                final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+                if (certBytes == null) {
+                    continue;
+                }
 
-            final Certificate c = toCertificate(certBytes);
-            nonCaEntries.add(alias);
+                final Certificate c = toCertificate(certBytes);
+                nonCaEntries.add(alias);
 
-            if (cert.equals(c)) {
-                return alias;
+                if (cert.equals(c)) {
+                    return alias;
+                }
             }
         }
 
@@ -472,19 +474,22 @@
          * PrivateKeyEntry we looked at above.
          */
         final String[] caAliases = mKeyStore.saw(Credentials.CA_CERTIFICATE);
-        for (String alias : caAliases) {
-            if (nonCaEntries.contains(alias)) {
-                continue;
-            }
+        if (certAliases != null) {
+            for (String alias : caAliases) {
+                if (nonCaEntries.contains(alias)) {
+                    continue;
+                }
 
-            final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
-            if (certBytes == null) {
-                continue;
-            }
+                final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+                if (certBytes == null) {
+                    continue;
+                }
 
-            final Certificate c = toCertificate(mKeyStore.get(Credentials.CA_CERTIFICATE + alias));
-            if (cert.equals(c)) {
-                return alias;
+                final Certificate c =
+                        toCertificate(mKeyStore.get(Credentials.CA_CERTIFICATE + alias));
+                if (cert.equals(c)) {
+                    return alias;
+                }
             }
         }
 
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 4b69317..12c0ed8 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -87,9 +87,22 @@
         }
     }
 
-    public boolean put(String key, byte[] value) {
+    public boolean put(String key, byte[] value, int uid) {
         try {
-            return mBinder.insert(key, value, -1) == NO_ERROR;
+            return mBinder.insert(key, value, uid) == NO_ERROR;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+    public boolean put(String key, byte[] value) {
+        return put(key, value, -1);
+    }
+
+    public boolean delete(String key, int uid) {
+        try {
+            return mBinder.del(key, uid) == NO_ERROR;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
@@ -97,8 +110,12 @@
     }
 
     public boolean delete(String key) {
+        return delete(key, -1);
+    }
+
+    public boolean contains(String key, int uid) {
         try {
-            return mBinder.del(key, -1) == NO_ERROR;
+            return mBinder.exist(key, uid) == NO_ERROR;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
@@ -106,23 +123,22 @@
     }
 
     public boolean contains(String key) {
-        try {
-            return mBinder.exist(key, -1) == NO_ERROR;
-        } catch (RemoteException e) {
-            Log.w(TAG, "Cannot connect to keystore", e);
-            return false;
-        }
+        return contains(key, -1);
     }
 
-    public String[] saw(String prefix) {
+    public String[] saw(String prefix, int uid) {
         try {
-            return mBinder.saw(prefix, -1);
+            return mBinder.saw(prefix, uid);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
         }
     }
 
+    public String[] saw(String prefix) {
+        return saw(prefix, -1);
+    }
+
     public boolean reset() {
         try {
             return mBinder.reset() == NO_ERROR;
@@ -169,9 +185,22 @@
         }
     }
 
-    public boolean generate(String key) {
+    public boolean generate(String key, int uid) {
         try {
-            return mBinder.generate(key, -1) == NO_ERROR;
+            return mBinder.generate(key, uid) == NO_ERROR;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+    public boolean generate(String key) {
+        return generate(key, -1);
+    }
+
+    public boolean importKey(String keyName, byte[] key, int uid) {
+        try {
+            return mBinder.import_key(keyName, key, uid) == NO_ERROR;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
@@ -179,12 +208,7 @@
     }
 
     public boolean importKey(String keyName, byte[] key) {
-        try {
-            return mBinder.import_key(keyName, key, -1) == NO_ERROR;
-        } catch (RemoteException e) {
-            Log.w(TAG, "Cannot connect to keystore", e);
-            return false;
-        }
+        return importKey(keyName, key, -1);
     }
 
     public byte[] getPubkey(String key) {
@@ -196,15 +220,19 @@
         }
     }
 
-    public boolean delKey(String key) {
+    public boolean delKey(String key, int uid) {
         try {
-            return mBinder.del_key(key, -1) == NO_ERROR;
+            return mBinder.del_key(key, uid) == NO_ERROR;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
         }
     }
 
+    public boolean delKey(String key) {
+        return delKey(key, -1);
+    }
+
     public byte[] sign(String key, byte[] data) {
         try {
             return mBinder.sign(key, data);
@@ -259,6 +287,15 @@
         }
     }
 
+    public boolean duplicate(String srcKey, int srcUid, String destKey, int destUid) {
+        try {
+            return mBinder.duplicate(srcKey, srcUid, destKey, destUid) == NO_ERROR;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
     public int getLastError() {
         return mError;
     }
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
index cd031b4..69007c4 100644
--- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
@@ -67,7 +67,9 @@
         assertTrue(mAndroidKeyStore.password("1111"));
         assertTrue(mAndroidKeyStore.isUnlocked());
 
-        assertEquals(0, mAndroidKeyStore.saw("").length);
+        String[] aliases = mAndroidKeyStore.saw("");
+        assertNotNull(aliases);
+        assertEquals(0, aliases.length);
 
         mGenerator = java.security.KeyPairGenerator.getInstance(AndroidKeyPairGenerator.NAME);
     }
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 07a2d7b..1de1eaf 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -17,6 +17,7 @@
 package android.security;
 
 import android.app.Activity;
+import android.os.Process;
 import android.security.KeyStore;
 import android.test.ActivityUnitTestCase;
 import android.test.AssertionFailedError;
@@ -128,7 +129,7 @@
         super.tearDown();
     }
 
-    public void teststate() throws Exception {
+    public void testState() throws Exception {
         assertEquals(KeyStore.State.UNINITIALIZED, mKeyStore.state());
     }
 
@@ -154,6 +155,24 @@
         assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
     }
 
+    public void testPut_grantedUid_Wifi() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        mKeyStore.password(TEST_PASSWD);
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testPut_ungrantedUid_Bluetooth() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        mKeyStore.password(TEST_PASSWD);
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
     public void testI18n() throws Exception {
         assertFalse(mKeyStore.put(TEST_I18N_KEY, TEST_I18N_VALUE));
         assertFalse(mKeyStore.contains(TEST_I18N_KEY));
@@ -167,22 +186,64 @@
         mKeyStore.password(TEST_PASSWD);
         assertFalse(mKeyStore.delete(TEST_KEYNAME));
 
-        mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE);
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE));
         assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
         assertTrue(mKeyStore.delete(TEST_KEYNAME));
         assertNull(mKeyStore.get(TEST_KEYNAME));
     }
 
+    public void testDelete_grantedUid_Wifi() throws Exception {
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
+        mKeyStore.password(TEST_PASSWD);
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertTrue(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testDelete_ungrantedUid_Bluetooth() throws Exception {
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        mKeyStore.password(TEST_PASSWD);
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
     public void testContains() throws Exception {
         assertFalse(mKeyStore.contains(TEST_KEYNAME));
 
-        mKeyStore.password(TEST_PASSWD);
+        assertTrue(mKeyStore.password(TEST_PASSWD));
         assertFalse(mKeyStore.contains(TEST_KEYNAME));
 
-        mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE);
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE));
         assertTrue(mKeyStore.contains(TEST_KEYNAME));
     }
 
+    public void testContains_grantedUid_Wifi() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testContains_grantedUid_Bluetooth() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
     public void testSaw() throws Exception {
         String[] emptyResult = mKeyStore.saw(TEST_KEYNAME);
         assertNotNull(emptyResult);
@@ -198,6 +259,48 @@
                      new HashSet(Arrays.asList(results)));
     }
 
+    public void testSaw_ungrantedUid_Bluetooth() throws Exception {
+        String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID);
+        assertNull(results1);
+
+        mKeyStore.password(TEST_PASSWD);
+        mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE);
+        mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE);
+
+        String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID);
+        assertNull(results2);
+    }
+
+    public void testSaw_grantedUid_Wifi() throws Exception {
+        String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.WIFI_UID);
+        assertNotNull(results1);
+        assertEquals(0, results1.length);
+
+        mKeyStore.password(TEST_PASSWD);
+        mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.WIFI_UID);
+        mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.WIFI_UID);
+
+        String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.WIFI_UID);
+        assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
+                                               TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
+                     new HashSet(Arrays.asList(results2)));
+    }
+
+    public void testSaw_grantedUid_Vpn() throws Exception {
+        String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.VPN_UID);
+        assertNotNull(results1);
+        assertEquals(0, results1.length);
+
+        mKeyStore.password(TEST_PASSWD);
+        mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.VPN_UID);
+        mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.VPN_UID);
+
+        String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.VPN_UID);
+        assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
+                                               TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
+                     new HashSet(Arrays.asList(results2)));
+    }
+
     public void testLock() throws Exception {
         assertFalse(mKeyStore.lock());
 
@@ -239,17 +342,57 @@
     }
 
     public void testGenerate_Success() throws Exception {
-        mKeyStore.password(TEST_PASSWD);
+        assertTrue(mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to generate key when unlocked",
                 mKeyStore.generate(TEST_KEYNAME));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testGenerate_grantedUid_Wifi_Success() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key when unlocked",
+                mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+    }
+
+    public void testGenerate_ungrantedUid_Bluetooth_Failure() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertFalse(mKeyStore.generate(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
     }
 
     public void testImport_Success() throws Exception {
-        mKeyStore.password(TEST_PASSWD);
+        assertTrue(mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to import key when unlocked",
                 mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testImport_grantedUid_Wifi_Success() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to import key when unlocked",
+                mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+    }
+
+    public void testImport_ungrantedUid_Bluetooth_Failure() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertFalse(mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
     }
 
     public void testImport_Failure_BadEncoding() throws Exception {
@@ -257,12 +400,15 @@
 
         assertFalse("Invalid DER-encoded key should not be imported",
                 mKeyStore.importKey(TEST_KEYNAME, TEST_DATA));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
     }
 
     public void testSign_Success() throws Exception {
         mKeyStore.password(TEST_PASSWD);
 
         assertTrue(mKeyStore.generate(TEST_KEYNAME));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
         final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
 
         assertNotNull("Signature should not be null", signature);
@@ -272,6 +418,7 @@
         mKeyStore.password(TEST_PASSWD);
 
         assertTrue(mKeyStore.generate(TEST_KEYNAME));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
         final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
 
         assertNotNull("Signature should not be null", signature);
@@ -406,6 +553,62 @@
                 mKeyStore.ungrant(TEST_KEYNAME, 0));
     }
 
+    public void testDuplicate_grantedUid_Wifi_Success() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.generate(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+
+        // source doesn't exist
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME1, -1, TEST_KEYNAME1, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
+
+        // Copy from current UID to granted UID
+        assertTrue(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME1, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME1));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME1, Process.WIFI_UID));
+
+        // Copy from granted UID to same granted UID
+        assertTrue(mKeyStore.duplicate(TEST_KEYNAME1, Process.WIFI_UID, TEST_KEYNAME2,
+                Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME2, Process.WIFI_UID));
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME1, Process.WIFI_UID, TEST_KEYNAME2,
+                Process.WIFI_UID));
+
+        assertTrue(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, -1));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME1));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME2));
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, -1));
+    }
+
+    public void testDuplicate_ungrantedUid_Bluetooth_Failure() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.generate(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME, Process.BLUETOOTH_UID, TEST_KEYNAME2,
+                Process.BLUETOOTH_UID));
+
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
     /**
      * The amount of time to allow before and after expected time for variance
      * in timing tests.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 7ac314b..b0561df 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -63,6 +63,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -743,6 +744,9 @@
         // correct directory.
         mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
         mBaseStateDir.mkdirs();
+        if (!SELinux.restorecon(mBaseStateDir)) {
+            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+        }
         mDataDir = Environment.getDownloadCacheDirectory();
 
         mPasswordHashFile = new File(mBaseStateDir, "pwhash");
@@ -2133,6 +2137,10 @@
                         ParcelFileDescriptor.MODE_CREATE |
                         ParcelFileDescriptor.MODE_TRUNCATE);
 
+                if (!SELinux.restorecon(mBackupDataName)) {
+                    Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
+                }
+
                 mNewState = ParcelFileDescriptor.open(mNewStateName,
                         ParcelFileDescriptor.MODE_READ_WRITE |
                         ParcelFileDescriptor.MODE_CREATE |
@@ -3795,7 +3803,7 @@
                 b.append(String.format(" %9d ", info.size));
 
                 Date stamp = new Date(info.mtime);
-                b.append(new SimpleDateFormat("MMM dd kk:mm:ss ").format(stamp));
+                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
 
                 b.append(info.packageName);
                 b.append(" :: ");
@@ -4572,6 +4580,10 @@
                             ParcelFileDescriptor.MODE_CREATE |
                             ParcelFileDescriptor.MODE_TRUNCATE);
 
+                if (!SELinux.restorecon(mBackupDataName)) {
+                    Slog.e(TAG, "SElinux restorecon failed for " + mBackupDataName);
+                }
+
                 if (mTransport.getRestoreData(mBackupData) != BackupConstants.TRANSPORT_OK) {
                     // Transport-level failure, so we wind everything up and
                     // terminate the restore operation.
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index b2a8ad8..1663106 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -29,6 +29,7 @@
 import android.os.BatteryManager;
 import android.os.Debug;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
 import android.os.ServiceManager;
@@ -114,6 +115,10 @@
      * Used for scheduling monitor callbacks and checking memory usage.
      */
     final class HeartbeatHandler extends Handler {
+        HeartbeatHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -183,7 +188,9 @@
 
     private Watchdog() {
         super("watchdog");
-        mHandler = new HeartbeatHandler();
+        // Explicitly bind the HeartbeatHandler to run on the ServerThread, so
+        // that it can't get accidentally bound to another thread.
+        mHandler = new HeartbeatHandler(Looper.getMainLooper());
     }
 
     public void init(Context context, BatteryService battery,
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6e81e9d..c6efe15b9 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -2171,7 +2171,7 @@
             // the PID of the new process, or else throw a RuntimeException.
             Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                     app.processName, uid, uid, gids, debugFlags, mountExternal,
-                    app.info.targetSdkVersion, null, null);
+                    app.info.targetSdkVersion, app.info.seinfo, null);
 
             BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
             synchronized (bs) {
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 71a6a01..6a071ef 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -188,7 +188,7 @@
         }
     }
 
-    public int install(String name, int uid, int gid) {
+    public int install(String name, int uid, int gid, String seinfo) {
         StringBuilder builder = new StringBuilder("install");
         builder.append(' ');
         builder.append(name);
@@ -196,6 +196,8 @@
         builder.append(uid);
         builder.append(' ');
         builder.append(gid);
+        builder.append(' ');
+        builder.append(seinfo != null ? seinfo : "!");
         return execute(builder.toString());
     }
 
@@ -263,7 +265,7 @@
         return execute(builder.toString());
     }
 
-    public int createUserData(String name, int uid, int userId) {
+    public int createUserData(String name, int uid, int userId, String seinfo) {
         StringBuilder builder = new StringBuilder("mkuserdata");
         builder.append(' ');
         builder.append(name);
@@ -271,6 +273,8 @@
         builder.append(uid);
         builder.append(' ');
         builder.append(userId);
+        builder.append(' ');
+        builder.append(seinfo != null ? seinfo : "!");
         return execute(builder.toString());
     }
 
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 2238f17..b8324ee 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -352,6 +352,9 @@
     final HashMap<String, FeatureInfo> mAvailableFeatures =
             new HashMap<String, FeatureInfo>();
 
+    // If mac_permissions.xml was found for seinfo labeling.
+    boolean mFoundPolicyFile;
+
     // All available activities, for your resolving pleasure.
     final ActivityIntentResolver mActivities =
             new ActivityIntentResolver();
@@ -1020,6 +1023,8 @@
 
             readPermissions();
 
+            mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
+
             mRestoredSettings = mSettings.readLPw(sUserManager.getUsers(false),
                     mSdkVersion, mOnlyCore);
             long startTime = SystemClock.uptimeMillis();
@@ -3582,16 +3587,16 @@
         }
     }
 
-    private int createDataDirsLI(String packageName, int uid) {
+    private int createDataDirsLI(String packageName, int uid, String seinfo) {
         int[] users = sUserManager.getUserIds();
-        int res = mInstaller.install(packageName, uid, uid);
+        int res = mInstaller.install(packageName, uid, uid, seinfo);
         if (res < 0) {
             return res;
         }
         for (int user : users) {
             if (user != 0) {
                 res = mInstaller.createUserData(packageName,
-                        UserHandle.getUid(user, uid), user);
+                        UserHandle.getUid(user, uid), user, seinfo);
                 if (res < 0) {
                     return res;
                 }
@@ -3847,6 +3852,10 @@
                 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
 
+            if (mFoundPolicyFile) {
+                SELinuxMMAC.assignSeinfoValue(pkg);
+            }
+
             pkg.applicationInfo.uid = pkgSetting.appId;
             pkg.mExtras = pkgSetting;
 
@@ -3985,7 +3994,8 @@
                             recovered = true;
 
                             // And now re-install the app.
-                            ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
+                            ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
+                                                   pkg.applicationInfo.seinfo);
                             if (ret == -1) {
                                 // Ack should not happen!
                                 msg = prefix + pkg.packageName
@@ -4031,7 +4041,8 @@
                         Log.v(TAG, "Want this data dir: " + dataPath);
                 }
                 //invoke installer to do the actual installation
-                int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
+                int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
+                                           pkg.applicationInfo.seinfo);
                 if (ret < 0) {
                     // Error from installer
                     mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java
new file mode 100644
index 0000000..4bbdb5e
--- /dev/null
+++ b/services/java/com/android/server/pm/SELinuxMMAC.java
@@ -0,0 +1,294 @@
+/*
+ * 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.server.pm;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.Signature;
+import android.os.Environment;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+import java.util.HashMap;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Centralized access to SELinux MMAC (middleware MAC) implementation.
+ * {@hide}
+ */
+public final class SELinuxMMAC {
+
+    private static final String TAG = "SELinuxMMAC";
+
+    private static final boolean DEBUG_POLICY = false;
+    private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;
+
+    // Signature seinfo values read from policy.
+    private static final HashMap<Signature, String> sSigSeinfo =
+        new HashMap<Signature, String>();
+
+    // Package name seinfo values read from policy.
+    private static final HashMap<String, String> sPackageSeinfo =
+        new HashMap<String, String>();
+
+    // Locations of potential install policy files.
+    private static final File[] INSTALL_POLICY_FILE = {
+        new File(Environment.getDataDirectory(), "system/mac_permissions.xml"),
+        new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
+        null};
+
+    private static void flushInstallPolicy() {
+        sSigSeinfo.clear();
+        sPackageSeinfo.clear();
+    }
+
+    /**
+     * Parses an MMAC install policy from a predefined list of locations.
+     * @param none
+     * @return boolean indicating whether an install policy was correctly parsed.
+     */
+    public static boolean readInstallPolicy() {
+
+        return readInstallPolicy(INSTALL_POLICY_FILE);
+    }
+
+    /**
+     * Parses an MMAC install policy given as an argument.
+     * @param File object representing the path of the policy.
+     * @return boolean indicating whether the install policy was correctly parsed.
+     */
+    public static boolean readInstallPolicy(File policyFile) {
+
+        return readInstallPolicy(new File[]{policyFile,null});
+    }
+
+    private static boolean readInstallPolicy(File[] policyFiles) {
+
+        FileReader policyFile = null;
+        int i = 0;
+        while (policyFile == null && policyFiles != null && policyFiles[i] != null) {
+            try {
+                policyFile = new FileReader(policyFiles[i]);
+                break;
+            } catch (FileNotFoundException e) {
+                Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath());
+            }
+            i++;
+        }
+
+        if (policyFile == null) {
+            Slog.d(TAG, "No policy file found. All seinfo values will be null.");
+            return false;
+        }
+
+        Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());
+
+        flushInstallPolicy();
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(policyFile);
+
+            XmlUtils.beginDocument(parser, "policy");
+            while (true) {
+                XmlUtils.nextElement(parser);
+                if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+                    break;
+                }
+
+                String tagName = parser.getName();
+                if ("signer".equals(tagName)) {
+                    String cert = parser.getAttributeValue(null, "signature");
+                    if (cert == null) {
+                        Slog.w(TAG, "<signer> without signature at "
+                               + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    Signature signature;
+                    try {
+                        signature = new Signature(cert);
+                    } catch (IllegalArgumentException e) {
+                        Slog.w(TAG, "<signer> with bad signature at "
+                               + parser.getPositionDescription(), e);
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String seinfo = readSeinfoTag(parser);
+                    if (seinfo != null) {
+                        if (DEBUG_POLICY_INSTALL)
+                            Slog.i(TAG, "<signer> tag: (" + cert + ") assigned seinfo="
+                                   + seinfo);
+
+                        sSigSeinfo.put(signature, seinfo);
+                    }
+                } else if ("default".equals(tagName)) {
+                    String seinfo = readSeinfoTag(parser);
+                    if (seinfo != null) {
+                        if (DEBUG_POLICY_INSTALL)
+                            Slog.i(TAG, "<default> tag assigned seinfo=" + seinfo);
+
+                        // The 'null' signature is the default seinfo value
+                        sSigSeinfo.put(null, seinfo);
+                    }
+                } else if ("package".equals(tagName)) {
+                    String pkgName = parser.getAttributeValue(null, "name");
+                    if (pkgName == null) {
+                        Slog.w(TAG, "<package> without name at "
+                               + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String seinfo = readSeinfoTag(parser);
+                    if (seinfo != null) {
+                        if (DEBUG_POLICY_INSTALL)
+                            Slog.i(TAG, "<package> tag: (" + pkgName +
+                                   ") assigned seinfo=" + seinfo);
+
+                        sPackageSeinfo.put(pkgName, seinfo);
+                    }
+                } else {
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                }
+            }
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "Got execption parsing ", e);
+        } catch (IOException e) {
+            Slog.w(TAG, "Got execption parsing ", e);
+        }
+        try {
+            policyFile.close();
+        } catch (IOException e) {
+            //omit
+        }
+        return true;
+    }
+
+    private static String readSeinfoTag(XmlPullParser parser) throws
+            IOException, XmlPullParserException {
+
+        int type;
+        int outerDepth = parser.getDepth();
+        String seinfo = null;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG
+                   || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG
+                || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if ("seinfo".equals(tagName)) {
+                String seinfoValue = parser.getAttributeValue(null, "value");
+                if (validateValue(seinfoValue)) {
+                    seinfo = seinfoValue;
+                } else {
+                    Slog.w(TAG, "<seinfo> without valid value at "
+                           + parser.getPositionDescription());
+                }
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+        return seinfo;
+    }
+
+    /**
+     * General validation routine for tag values.
+     * Returns a boolean indicating if the passed string
+     * contains only letters or underscores.
+     */
+    private static boolean validateValue(String name) {
+        if (name == null)
+            return false;
+
+        final int N = name.length();
+        if (N == 0)
+            return false;
+
+        for (int i = 0; i < N; i++) {
+            final char c = name.charAt(i);
+            if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c != '_')) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Labels a package based on an seinfo tag from install policy.
+     * The label is attached to the ApplicationInfo instance of the package.
+     * @param PackageParser.Package object representing the package
+     *         to labeled.
+     * @return String holding the value of the seinfo label that was assigned.
+     *         Value may be null which indicates no seinfo label was assigned.
+     */
+    public static void assignSeinfoValue(PackageParser.Package pkg) {
+
+        /*
+         * Non system installed apps should be treated the same. This
+         * means that any post-loaded apk will be assigned the default
+         * tag, if one exists in the policy, else null, without respect
+         * to the signing key.
+         */
+        if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||
+            ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {
+
+            // We just want one of the signatures to match.
+            for (Signature s : pkg.mSignatures) {
+                if (s == null)
+                    continue;
+
+                if (sSigSeinfo.containsKey(s)) {
+                    String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(s);
+                    if (DEBUG_POLICY_INSTALL)
+                        Slog.i(TAG, "package (" + pkg.packageName +
+                               ") labeled with seinfo=" + seinfo);
+
+                    return;
+                }
+            }
+
+            // Check for seinfo labeled by package.
+            if (sPackageSeinfo.containsKey(pkg.packageName)) {
+                String seinfo = pkg.applicationInfo.seinfo = sPackageSeinfo.get(pkg.packageName);
+                if (DEBUG_POLICY_INSTALL)
+                    Slog.i(TAG, "package (" + pkg.packageName +
+                           ") labeled with seinfo=" + seinfo);
+                return;
+            }
+        }
+
+        // If we have a default seinfo value then great, otherwise
+        // we set a null object and that is what we started with.
+        String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(null);
+        if (DEBUG_POLICY_INSTALL)
+            Slog.i(TAG, "package (" + pkg.packageName +
+                   ") labeled with seinfo=" + (seinfo == null ? "null" : seinfo));
+    }
+}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 06f11bc..a472247 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -2337,7 +2337,8 @@
             ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle);
             // Need to create a data directory for all apps under this user.
             installer.createUserData(ps.name,
-                    UserHandle.getUid(userHandle, ps.appId), userHandle);
+                    UserHandle.getUid(userHandle, ps.appId), userHandle,
+                    ps.pkg.applicationInfo.seinfo);
         }
         readDefaultPreferredAppsLPw(userHandle);
         writePackageRestrictionsLPr(userHandle);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 0466c15..194c750 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -1514,7 +1514,11 @@
                     pos++;
                 }
                 if (pos >= N) {
-                    // All is good!
+                    // Z order is good.
+                    // The IM target window may be changed, so update the mTargetAppToken.
+                    if (imWin != null) {
+                        imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
+                    }
                     return false;
                 }
             }