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;
}
}