diff options
53 files changed, 1401 insertions, 1079 deletions
diff --git a/api/current.txt b/api/current.txt index 144f292..ed80e38 100644 --- a/api/current.txt +++ b/api/current.txt @@ -547,6 +547,7 @@ package android { field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6 field public static final int exported = 16842768; // 0x1010010 field public static final int extraTension = 16843371; // 0x101026b + field public static final int extractNativeLibs = 16843990; // 0x10104d6 field public static final int factor = 16843219; // 0x10101d3 field public static final int fadeDuration = 16843384; // 0x1010278 field public static final int fadeEnabled = 16843390; // 0x101027e @@ -8368,6 +8369,7 @@ package android.content.pm { field public static final int FLAG_ALLOW_TASK_REPARENTING = 32; // 0x20 field public static final int FLAG_DEBUGGABLE = 2; // 0x2 field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000 + field public static final int FLAG_EXTRACT_NATIVE_LIBS = 268435456; // 0x10000000 field public static final int FLAG_FACTORY_TEST = 16; // 0x10 field public static final int FLAG_FULL_BACKUP_ONLY = 67108864; // 0x4000000 field public static final int FLAG_HAS_CODE = 4; // 0x4 @@ -17515,7 +17517,7 @@ package android.net.http { method public static android.net.http.HttpResponseCache getInstalled(); method public int getNetworkCount(); method public int getRequestCount(); - method public static android.net.http.HttpResponseCache install(java.io.File, long) throws java.io.IOException; + method public static synchronized android.net.http.HttpResponseCache install(java.io.File, long) throws java.io.IOException; method public long maxSize(); method public java.net.CacheRequest put(java.net.URI, java.net.URLConnection) throws java.io.IOException; method public long size(); @@ -41519,7 +41521,7 @@ package java.lang { method public static double nextUp(double); method public static float nextUp(float); method public static double pow(double, double); - method public static synchronized double random(); + method public static double random(); method public static double rint(double); method public static long round(double); method public static int round(float); diff --git a/api/system-current.txt b/api/system-current.txt index 7ab0e48..8f1b8b2 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -617,6 +617,7 @@ package android { field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6 field public static final int exported = 16842768; // 0x1010010 field public static final int extraTension = 16843371; // 0x101026b + field public static final int extractNativeLibs = 16843990; // 0x10104d6 field public static final int factor = 16843219; // 0x10101d3 field public static final int fadeDuration = 16843384; // 0x1010278 field public static final int fadeEnabled = 16843390; // 0x101027e @@ -8616,6 +8617,7 @@ package android.content.pm { field public static final int FLAG_ALLOW_TASK_REPARENTING = 32; // 0x20 field public static final int FLAG_DEBUGGABLE = 2; // 0x2 field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000 + field public static final int FLAG_EXTRACT_NATIVE_LIBS = 268435456; // 0x10000000 field public static final int FLAG_FACTORY_TEST = 16; // 0x10 field public static final int FLAG_FULL_BACKUP_ONLY = 67108864; // 0x4000000 field public static final int FLAG_HAS_CODE = 4; // 0x4 @@ -18843,7 +18845,7 @@ package android.net.http { method public static android.net.http.HttpResponseCache getInstalled(); method public int getNetworkCount(); method public int getRequestCount(); - method public static android.net.http.HttpResponseCache install(java.io.File, long) throws java.io.IOException; + method public static synchronized android.net.http.HttpResponseCache install(java.io.File, long) throws java.io.IOException; method public long maxSize(); method public java.net.CacheRequest put(java.net.URI, java.net.URLConnection) throws java.io.IOException; method public long size(); @@ -44055,7 +44057,7 @@ package java.lang { method public static double nextUp(double); method public static float nextUp(float); method public static double pow(double, double); - method public static synchronized double random(); + method public static double random(); method public static double rint(double); method public static long round(double); method public static int round(float); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index beb244b..9269f60 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -97,7 +97,7 @@ import android.view.ViewRootImpl; import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerGlobal; -import android.renderscript.RenderScript; +import android.renderscript.RenderScriptCacheDir; import android.security.AndroidKeyStoreProvider; import com.android.internal.app.IVoiceInteractor; @@ -3187,7 +3187,7 @@ public final class ActivityThread { if (cv == null) { mThumbnailCanvas = cv = new Canvas(); } - + cv.setBitmap(thumbnail); if (!r.activity.onCreateThumbnail(thumbnail, cv)) { mAvailThumbnailBitmap = thumbnail; @@ -3485,12 +3485,12 @@ public final class ActivityThread { private void handleWindowVisibility(IBinder token, boolean show) { ActivityClientRecord r = mActivities.get(token); - + if (r == null) { Log.w(TAG, "handleWindowVisibility: no activity for token " + token); return; } - + if (!show && !r.stopped) { performStopActivityInner(r, null, show, false); } else if (show && r.stopped) { @@ -3918,10 +3918,10 @@ public final class ActivityThread { } } } - + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Relaunching activity " + tmp.token + ": changedConfig=" + changedConfig); - + // If there was a pending configuration change, execute it first. if (changedConfig != null) { mCurDefaultDisplayDpi = changedConfig.densityDpi; @@ -4118,7 +4118,7 @@ public final class ActivityThread { if (config == null) { return; } - + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: " + config); @@ -4166,7 +4166,7 @@ public final class ActivityThread { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: " + r.activityInfo.name); - + performConfigurationChanged(r.activity, mCompatConfiguration); freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(mCompatConfiguration)); @@ -4247,7 +4247,7 @@ public final class ActivityThread { ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo); } - + final void handleLowMemory() { ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null); @@ -4294,10 +4294,10 @@ public final class ActivityThread { String[] packages = getPackageManager().getPackagesForUid(uid); // If there are several packages in this application we won't - // initialize the graphics disk caches + // initialize the graphics disk caches if (packages != null && packages.length == 1) { HardwareRenderer.setupDiskCache(cacheDir); - RenderScript.setupDiskCache(cacheDir); + RenderScriptCacheDir.setupDiskCache(cacheDir); } } catch (RemoteException e) { // Ignore @@ -5222,7 +5222,7 @@ public final class ActivityThread { if (mPendingConfiguration == null || mPendingConfiguration.isOtherSeqNewer(newConfig)) { mPendingConfiguration = newConfig; - + sendMessage(H.CONFIGURATION_CHANGED, newConfig); } } diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java index 6c2511e..8692336 100644 --- a/core/java/android/app/ApplicationErrorReport.java +++ b/core/java/android/app/ApplicationErrorReport.java @@ -235,10 +235,13 @@ public class ApplicationErrorReport implements Parcelable { dest.writeString(processName); dest.writeLong(time); dest.writeInt(systemApp ? 1 : 0); + dest.writeInt(crashInfo != null ? 1 : 0); switch (type) { case TYPE_CRASH: - crashInfo.writeToParcel(dest, flags); + if (crashInfo != null) { + crashInfo.writeToParcel(dest, flags); + } break; case TYPE_ANR: anrInfo.writeToParcel(dest, flags); @@ -259,10 +262,11 @@ public class ApplicationErrorReport implements Parcelable { processName = in.readString(); time = in.readLong(); systemApp = in.readInt() == 1; + boolean hasCrashInfo = in.readInt() == 1; switch (type) { case TYPE_CRASH: - crashInfo = new CrashInfo(in); + crashInfo = hasCrashInfo ? new CrashInfo(in) : null; anrInfo = null; batteryInfo = null; runningServiceInfo = null; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index b3c558b..4a087da 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -346,6 +346,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27; /** + * When set installer extracts native libs from .apk files. + */ + public static final int FLAG_EXTRACT_NATIVE_LIBS = 1<<28; + + /** * Value for {@link #flags}: true if code from this application will need to be * loaded into other applications' processes. On devices that support multiple * instruction sets, this implies the code might be loaded into a process that's diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 5320929..53aa6ff 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -268,6 +268,7 @@ public class PackageParser { public final boolean coreApp; public final boolean multiArch; + public final boolean extractNativeLibs; public PackageLite(String codePath, ApkLite baseApk, String[] splitNames, String[] splitCodePaths, int[] splitRevisionCodes) { @@ -283,6 +284,7 @@ public class PackageParser { this.splitRevisionCodes = splitRevisionCodes; this.coreApp = baseApk.coreApp; this.multiArch = baseApk.multiArch; + this.extractNativeLibs = baseApk.extractNativeLibs; } public List<String> getAllCodePaths() { @@ -309,10 +311,12 @@ public class PackageParser { public final Signature[] signatures; public final boolean coreApp; public final boolean multiArch; + public final boolean extractNativeLibs; public ApkLite(String codePath, String packageName, String splitName, int versionCode, int revisionCode, int installLocation, List<VerifierInfo> verifiers, - Signature[] signatures, boolean coreApp, boolean multiArch) { + Signature[] signatures, boolean coreApp, boolean multiArch, + boolean extractNativeLibs) { this.codePath = codePath; this.packageName = packageName; this.splitName = splitName; @@ -323,6 +327,7 @@ public class PackageParser { this.signatures = signatures; this.coreApp = coreApp; this.multiArch = multiArch; + this.extractNativeLibs = extractNativeLibs; } } @@ -1269,6 +1274,7 @@ public class PackageParser { int revisionCode = 0; boolean coreApp = false; boolean multiArch = false; + boolean extractNativeLibs = true; for (int i = 0; i < attrs.getAttributeCount(); i++) { final String attr = attrs.getAttributeName(i); @@ -1307,14 +1313,17 @@ public class PackageParser { final String attr = attrs.getAttributeName(i); if ("multiArch".equals(attr)) { multiArch = attrs.getAttributeBooleanValue(i, false); - break; + } + if ("extractNativeLibs".equals(attr)) { + extractNativeLibs = attrs.getAttributeBooleanValue(i, true); } } } } return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode, - revisionCode, installLocation, verifiers, signatures, coreApp, multiArch); + revisionCode, installLocation, verifiers, signatures, coreApp, multiArch, + extractNativeLibs); } /** @@ -2567,6 +2576,12 @@ public class PackageParser { ai.flags |= ApplicationInfo.FLAG_MULTIARCH; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs, + true)) { + ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS; + } + String str; str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestApplication_permission, 0); diff --git a/core/java/android/inputmethodservice/ExtractEditLayout.java b/core/java/android/inputmethodservice/ExtractEditLayout.java index 5696839..e902443 100644 --- a/core/java/android/inputmethodservice/ExtractEditLayout.java +++ b/core/java/android/inputmethodservice/ExtractEditLayout.java @@ -163,6 +163,8 @@ public class ExtractEditLayout extends LinearLayout { mCallback.onDestroyActionMode(this); mCallback = null; + mMenu.close(); + mExtractActionButton.setVisibility(VISIBLE); mEditButton.setVisibility(INVISIBLE); diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index e94a312..6176399 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -171,6 +171,9 @@ public final class KeymasterDefs { public static final int KM_KEY_FORMAT_PKCS12 = 2; public static final int KM_KEY_FORMAT_RAW = 3; + // User authenticators. + public static final int HW_AUTH_PASSWORD = 1 << 0; + // Error codes. public static final int KM_ERROR_OK = 0; public static final int KM_ERROR_ROOT_OF_TRUST_ALREADY_SET = -1; diff --git a/core/java/android/text/SpanSet.java b/core/java/android/text/SpanSet.java index 3ca6033..00f1493 100644 --- a/core/java/android/text/SpanSet.java +++ b/core/java/android/text/SpanSet.java @@ -17,6 +17,7 @@ package android.text; import java.lang.reflect.Array; +import java.util.Arrays; /** * A cached set of spans. Caches the result of {@link Spanned#getSpans(int, int, Class)} and then @@ -54,6 +55,7 @@ public class SpanSet<E> { spanFlags = new int[length]; } + int prevNumberOfSpans = numberOfSpans; numberOfSpans = 0; for (int i = 0; i < length; i++) { final E span = allSpans[i]; @@ -71,6 +73,12 @@ public class SpanSet<E> { numberOfSpans++; } + + // cleanup extra spans left over from previous init() call + if (numberOfSpans < prevNumberOfSpans) { + // prevNumberofSpans was > 0, therefore spans != null + Arrays.fill(spans, numberOfSpans, prevNumberOfSpans, null); + } } /** @@ -103,9 +111,8 @@ public class SpanSet<E> { * Removes all internal references to the spans to avoid memory leaks. */ public void recycle() { - // The spans array is guaranteed to be not null when numberOfSpans is > 0 - for (int i = 0; i < numberOfSpans; i++) { - spans[i] = null; // prevent a leak: no reference kept when TextLine is recycled + if (spans != null) { + Arrays.fill(spans, 0, numberOfSpans, null); } } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index de1bbc7..358dbde 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1812,9 +1812,7 @@ public class ProgressBar extends View { } if (mRefreshProgressRunnable != null) { removeCallbacks(mRefreshProgressRunnable); - } - if (mRefreshProgressRunnable != null && mRefreshIsPosted) { - removeCallbacks(mRefreshProgressRunnable); + mRefreshIsPosted = false; } if (mAccessibilityEventSender != null) { removeCallbacks(mAccessibilityEventSender); diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index 02f675c..f479f4f 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -33,6 +33,7 @@ import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; import android.os.Build; import android.os.SELinux; +import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; import android.util.Slog; @@ -74,6 +75,7 @@ public class NativeLibraryHelper { final long[] apkHandles; final boolean multiArch; + final boolean extractNativeLibs; public static Handle create(File packageFile) throws IOException { try { @@ -86,14 +88,16 @@ public class NativeLibraryHelper { public static Handle create(Package pkg) throws IOException { return create(pkg.getAllCodePaths(), - (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0); + (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0, + (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0); } public static Handle create(PackageLite lite) throws IOException { - return create(lite.getAllCodePaths(), lite.multiArch); + return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs); } - private static Handle create(List<String> codePaths, boolean multiArch) throws IOException { + private static Handle create(List<String> codePaths, boolean multiArch, + boolean extractNativeLibs) throws IOException { final int size = codePaths.size(); final long[] apkHandles = new long[size]; for (int i = 0; i < size; i++) { @@ -108,12 +112,13 @@ public class NativeLibraryHelper { } } - return new Handle(apkHandles, multiArch); + return new Handle(apkHandles, multiArch, extractNativeLibs); } - Handle(long[] apkHandles, boolean multiArch) { + Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs) { this.apkHandles = apkHandles; this.multiArch = multiArch; + this.extractNativeLibs = extractNativeLibs; mGuard.open("close"); } @@ -146,8 +151,8 @@ public class NativeLibraryHelper { private static native long nativeSumNativeBinaries(long handle, String cpuAbi); - private native static int nativeCopyNativeBinaries(long handle, - String sharedLibraryPath, String abiToCopy); + private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath, + String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge); private static long sumNativeBinaries(Handle handle, String abi) { long sum = 0; @@ -167,7 +172,8 @@ public class NativeLibraryHelper { */ public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) { for (long apkHandle : handle.apkHandles) { - int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi); + int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi, + handle.extractNativeLibs, HAS_NATIVE_BRIDGE); if (res != INSTALL_SUCCEEDED) { return res; } @@ -218,7 +224,8 @@ public class NativeLibraryHelper { /** * Remove the native binaries of a given package. This deletes the files */ - public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir) { + public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot, + boolean deleteRootDir) { if (DEBUG_NATIVE) { Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath()); } @@ -247,7 +254,8 @@ public class NativeLibraryHelper { // asked to or this will prevent installation of future updates. if (deleteRootDir) { if (!nativeLibraryRoot.delete()) { - Slog.w(TAG, "Could not delete native binary directory: " + nativeLibraryRoot.getPath()); + Slog.w(TAG, "Could not delete native binary directory: " + + nativeLibraryRoot.getPath()); } } } @@ -416,6 +424,9 @@ public class NativeLibraryHelper { // We don't care about the other return values for now. private static final int BITCODE_PRESENT = 1; + private static final boolean HAS_NATIVE_BRIDGE = + !"0".equals(SystemProperties.get("ro.dalvik.vm.native.bridge", "0")); + private static native int hasRenderscriptBitcode(long apkHandle); public static boolean hasRenderscriptBitcode(Handle handle) throws IOException { diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java index a4cdf19..671bf24 100644 --- a/core/java/com/android/internal/os/InstallerConnection.java +++ b/core/java/com/android/internal/os/InstallerConnection.java @@ -90,12 +90,15 @@ public class InstallerConnection { } } - public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) { - return dexopt(apkPath, uid, isPublic, "*", instructionSet, false, false, null); + public int dexopt(String apkPath, int uid, boolean isPublic, + String instructionSet, int dexoptNeeded) { + return dexopt(apkPath, uid, isPublic, "*", instructionSet, dexoptNeeded, + false, false, null); } public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName, - String instructionSet, boolean vmSafeMode, boolean debuggable, String outputPath) { + String instructionSet, int dexoptNeeded, boolean vmSafeMode, + boolean debuggable, String outputPath) { StringBuilder builder = new StringBuilder("dexopt"); builder.append(' '); builder.append(apkPath); @@ -106,6 +109,8 @@ public class InstallerConnection { builder.append(pkgName); builder.append(' '); builder.append(instructionSet); + builder.append(' '); + builder.append(dexoptNeeded); builder.append(vmSafeMode ? " 1" : " 0"); builder.append(debuggable ? " 1" : " 0"); builder.append(' '); @@ -113,25 +118,6 @@ public class InstallerConnection { return execute(builder.toString()); } - public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) { - return patchoat(apkPath, uid, isPublic, "*", instructionSet); - } - - public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName, - String instructionSet) { - StringBuilder builder = new StringBuilder("patchoat"); - builder.append(' '); - builder.append(apkPath); - builder.append(' '); - builder.append(uid); - builder.append(isPublic ? " 1" : " 0"); - builder.append(' '); - builder.append(pkgName); - builder.append(' '); - builder.append(instructionSet); - return execute(builder.toString()); - } - private boolean connect() { if (mSocket != null) { return true; diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 98638ed..50ddbd1 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -465,12 +465,11 @@ public class ZygoteInit { try { for (String classPathElement : classPathElements) { - final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet, - false /* defer */); - if (dexopt == DexFile.DEXOPT_NEEDED) { - installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet); - } else if (dexopt == DexFile.PATCHOAT_NEEDED) { - installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet); + final int dexoptNeeded = DexFile.getDexOptNeeded( + classPathElement, "*", instructionSet, false /* defer */); + if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { + installer.dexopt(classPathElement, Process.SYSTEM_UID, false, + instructionSet, dexoptNeeded); } } } catch (IOException ioe) { diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index d826303..0bfc0c9 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -586,6 +586,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) char localeOption[sizeof("-Duser.locale=") + PROPERTY_VALUE_MAX]; char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX]; char nativeBridgeLibrary[sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX]; + char cpuAbiListBuf[sizeof("--cpu-abilist=") + PROPERTY_VALUE_MAX]; bool checkJni = false; property_get("dalvik.vm.checkjni", propBuf, ""); @@ -859,6 +860,18 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) // Dalvik-cache pruning counter. parseRuntimeOption("dalvik.vm.zygote.max-boot-retry", cachePruneBuf, "-Xzygote-max-boot-retry="); +#if defined(__LP64__) + const char* cpu_abilist_property_name = "ro.product.cpu.abilist64"; +#else + const char* cpu_abilist_property_name = "ro.product.cpu.abilist32"; +#endif // defined(__LP64__) + property_get(cpu_abilist_property_name, propBuf, ""); + if (propBuf[0] == '\0') { + ALOGE("%s is not expected to be empty", cpu_abilist_property_name); + return -1; + } + snprintf(cpuAbiListBuf, sizeof(cpuAbiListBuf), "--cpu-abilist=%s", propBuf); + addOption(cpuAbiListBuf); initArgs.version = JNI_VERSION_1_4; initArgs.options = mOptions.editArray(); diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index 3c1993e..9307ff9 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -33,6 +33,7 @@ #include <string.h> #include <time.h> #include <unistd.h> +#include <inttypes.h> #include <sys/stat.h> #include <sys/types.h> @@ -173,7 +174,11 @@ sumFiles(JNIEnv*, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char static install_status_t copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName) { - jstring* javaNativeLibPath = (jstring*) arg; + void** args = reinterpret_cast<void**>(arg); + jstring* javaNativeLibPath = (jstring*) args[0]; + jboolean extractNativeLibs = *(jboolean*) args[1]; + jboolean hasNativeBridge = *(jboolean*) args[2]; + ScopedUtfChars nativeLibPath(env, *javaNativeLibPath); size_t uncompLen; @@ -181,13 +186,31 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr long crc; time_t modTime; - if (!zipFile->getEntryInfo(zipEntry, NULL, &uncompLen, NULL, NULL, &when, &crc)) { + int method; + off64_t offset; + + if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, NULL, &offset, &when, &crc)) { ALOGD("Couldn't read zip entry info\n"); return INSTALL_FAILED_INVALID_APK; - } else { - struct tm t; - ZipUtils::zipTimeToTimespec(when, &t); - modTime = mktime(&t); + } + + if (!extractNativeLibs) { + // check if library is uncompressed and page-aligned + if (method != ZipFileRO::kCompressStored) { + ALOGD("Library '%s' is compressed - will not be able to open it directly from apk.\n", + fileName); + return INSTALL_FAILED_INVALID_APK; + } + + if (offset % PAGE_SIZE != 0) { + ALOGD("Library '%s' is not page-aligned - will not be able to open it directly from" + " apk.\n", fileName); + return INSTALL_FAILED_INVALID_APK; + } + + if (!hasNativeBridge) { + return INSTALL_SUCCEEDED; + } } // Build local file path @@ -208,6 +231,9 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr } // Only copy out the native file if it's different. + struct tm t; + ZipUtils::zipTimeToTimespec(when, &t); + modTime = mktime(&t); struct stat64 st; if (!isFileDifferent(localFileName, uncompLen, modTime, crc, &st)) { return INSTALL_SUCCEEDED; @@ -465,10 +491,12 @@ static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supported static jint com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz, - jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi) + jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi, + jboolean extractNativeLibs, jboolean hasNativeBridge) { + void* args[] = { &javaNativeLibPath, &extractNativeLibs, &hasNativeBridge }; return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi, - copyFileIfChanged, &javaNativeLibPath); + copyFileIfChanged, reinterpret_cast<void*>(args)); } static jlong @@ -548,7 +576,7 @@ static JNINativeMethod gMethods[] = { "(J)V", (void *)com_android_internal_content_NativeLibraryHelper_close}, {"nativeCopyNativeBinaries", - "(JLjava/lang/String;Ljava/lang/String;)I", + "(JLjava/lang/String;Ljava/lang/String;ZZ)I", (void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries}, {"nativeSumNativeBinaries", "(JLjava/lang/String;)J", diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index ea592cf..f69772a 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1021,6 +1021,10 @@ <p>The default value of this attribute is <code>false</code>. --> <attr name="resumeWhilePausing" format="boolean" /> + <!-- When set installer will extract native libraries. If set to false + libraries in the apk must be stored and page-aligned. --> + <attr name="extractNativeLibs" format="boolean"/> + <!-- The <code>manifest</code> tag is the root of an <code>AndroidManifest.xml</code> file, describing the contents of an Android package (.apk) file. One @@ -1151,8 +1155,8 @@ @hide --> <attr name="usesCleartextTraffic" /> <attr name="multiArch" /> + <attr name="extractNativeLibs" /> </declare-styleable> - <!-- The <code>permission</code> tag declares a security permission that can be used to control access from other packages to specific components or features in your package (or other packages). See the diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 2bb9aa8..bfd0d26 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2598,4 +2598,5 @@ <public type="style" name="Theme.DeviceDefault.Dialog.Alert" /> <public type="style" name="Theme.DeviceDefault.Light.Dialog.Alert" /> + <public type="attr" name="extractNativeLibs" /> </resources> diff --git a/core/tests/coretests/apks/install_jni_lib/Android.mk b/core/tests/coretests/apks/install_jni_lib/Android.mk index b61ea8e..7322e8d 100644 --- a/core/tests/coretests/apks/install_jni_lib/Android.mk +++ b/core/tests/coretests/apks/install_jni_lib/Android.mk @@ -23,6 +23,14 @@ LOCAL_SHARED_LIBRARIES := \ libnativehelper LOCAL_MODULE := libframeworks_coretests_jni + +# this does not prevent build system +# from installing library to /system/lib LOCAL_MODULE_TAGS := tests +# .. we want to avoid that... so we put it somewhere +# bionic linker cant find it without outside help (nativetests): +LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) + include $(BUILD_SHARED_LIBRARY) diff --git a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp index 957fc4a..e0b616c 100644 --- a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp +++ b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp @@ -27,8 +27,8 @@ static JNINativeMethod sMethods[] = { { "checkFunction", "()I", (void*) checkFunction }, }; -int register_com_android_framework_coretests_JNITests(JNIEnv* env) { - return jniRegisterNativeMethods(env, "com/android/framework/coretests/JNITests", sMethods, +int register_com_android_frameworks_coretests_JNITests(JNIEnv* env) { + return jniRegisterNativeMethods(env, "com/android/frameworks/coretests/JNITests", sMethods, NELEM(sMethods)); } @@ -46,7 +46,7 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { return JNI_ERR; } - if ((status = android::register_com_android_framework_coretests_JNITests(e)) < 0) { + if ((status = android::register_com_android_frameworks_coretests_JNITests(e)) < 0) { return JNI_ERR; } diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk new file mode 100644 index 0000000..5fa2405 --- /dev/null +++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := install_jni_lib_open_from_apk + +LOCAL_JNI_SHARED_LIBRARIES_ZIP_OPTIONS := -0 +LOCAL_PAGE_ALIGN_JNI_SHARED_LIBRARIES := true + +include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml b/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml new file mode 100644 index 0000000..190f894 --- /dev/null +++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.coretests.install_jni_lib_open_from_apk"> + + <application android:hasCode="true" android:label="@string/app_name" android:extractNativeLibs="false"> + <activity android:name="com.android.frameworks.coretests.OpenFromApkActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml b/core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml new file mode 100644 index 0000000..8c2a0bf --- /dev/null +++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 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. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_name">Load From Apk Test</string> +</resources> diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java new file mode 100644 index 0000000..4f9176c --- /dev/null +++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.frameworks.coretests; + +public class JNITests { + static { + System.loadLibrary("frameworks_coretests_jni"); + } + + public static native int checkFunction(); +} diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java new file mode 100644 index 0000000..524cad7 --- /dev/null +++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.frameworks.coretests; + +import android.app.Activity; +import android.widget.TextView; +import android.os.Bundle; + +public class OpenFromApkActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + TextView tv = new TextView(this); + + int i = JNITests.checkFunction(); + + tv.setText("All is well: i=" + i); + + setContentView(tv); + } + +} diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index dcc79be..55a8b4f 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -466,81 +466,83 @@ public class AndroidKeyStore extends KeyStoreSpi { throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString); } - if ((params.getAlgorithm() != null) && (params.getAlgorithm() != keyAlgorithm)) { - throw new KeyStoreException("Key algorithm mismatch. Key: " + keyAlgorithmString - + ", parameter spec: " - + KeyStoreKeyConstraints.Algorithm.toString(params.getAlgorithm())); - } - KeymasterArguments args = new KeymasterArguments(); args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeyStoreKeyConstraints.Algorithm.toKeymaster(keyAlgorithm)); - if (digest != null) { - // Digest available from JCA key algorithm - if (params.getDigest() != null) { - // Digest also specified in parameters -- check that these two match - if (digest != params.getDigest()) { - throw new KeyStoreException("Key digest mismatch. Key: " + keyAlgorithmString + @KeyStoreKeyConstraints.DigestEnum int digests; + if (params.isDigestsSpecified()) { + // Digest(s) specified in parameters + if (digest != null) { + // Digest also specified in the JCA key algorithm name. + if ((params.getDigests() & digest) != digest) { + throw new KeyStoreException("Key digest mismatch" + + ". Key: " + keyAlgorithmString + ", parameter spec: " - + KeyStoreKeyConstraints.Digest.toString(params.getDigest())); + + KeyStoreKeyConstraints.Digest.allToString(params.getDigests())); } } + digests = params.getDigests(); } else { - // Digest not available from JCA key algorithm - digest = params.getDigest(); + // No digest specified in parameters + if (digest != null) { + // Digest specified in the JCA key algorithm name. + digests = digest; + } else { + digests = 0; + } } - if (digest != null) { - args.addInt(KeymasterDefs.KM_TAG_DIGEST, - KeyStoreKeyConstraints.Digest.toKeymaster(digest)); + for (int keymasterDigest : KeyStoreKeyConstraints.Digest.allToKeymaster(digests)) { + args.addInt(KeymasterDefs.KM_TAG_DIGEST, keymasterDigest); + } + if (digests != 0) { + // TODO: Remove MAC length constraint once Keymaster API no longer requires it. + // This code will blow up if mode than one digest is specified. Integer digestOutputSizeBytes = KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest); if (digestOutputSizeBytes != null) { - // TODO: Remove MAC length constraint once Keymaster API no longer requires it. // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes); } } if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) { - if (digest == null) { - throw new IllegalStateException("Digest algorithm must be specified for key" - + " algorithm " + keyAlgorithmString); + if (digests == 0) { + throw new KeyStoreException("At least one digest algorithm must be specified" + + " for key algorithm " + keyAlgorithmString); } } - @KeyStoreKeyConstraints.PurposeEnum int purposes = (params.getPurposes() != null) - ? params.getPurposes() - : (KeyStoreKeyConstraints.Purpose.ENCRYPT - | KeyStoreKeyConstraints.Purpose.DECRYPT - | KeyStoreKeyConstraints.Purpose.SIGN - | KeyStoreKeyConstraints.Purpose.VERIFY); - for (int keymasterPurpose : - KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { - args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); - } - if (params.getBlockMode() != null) { - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, - KeyStoreKeyConstraints.BlockMode.toKeymaster(params.getBlockMode())); + @KeyStoreKeyConstraints.PurposeEnum int purposes = params.getPurposes(); + @KeyStoreKeyConstraints.BlockModeEnum int blockModes = params.getBlockModes(); + if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) + && (params.isRandomizedEncryptionRequired())) { + @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes = + blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES; + if (incompatibleBlockModes != 0) { + throw new KeyStoreException("Randomized encryption (IND-CPA) required but may be" + + " violated by block mode(s): " + + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes) + + ". See KeyStoreParameter documentation."); + } } - if (params.getPadding() != null) { - args.addInt(KeymasterDefs.KM_TAG_PADDING, - KeyStoreKeyConstraints.Padding.toKeymaster(params.getPadding())); + for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { + args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); } - if (params.getMaxUsesPerBoot() != null) { - args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, params.getMaxUsesPerBoot()); + for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) { + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode); } - if (params.getMinSecondsBetweenOperations() != null) { - args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS, - params.getMinSecondsBetweenOperations()); + for (int keymasterPadding : + KeyStoreKeyConstraints.Padding.allToKeymaster(params.getPaddings())) { + args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding); } - if (params.getUserAuthenticators().isEmpty()) { + if (params.getUserAuthenticators() == 0) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); } else { args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster( params.getUserAuthenticators())); } - if (params.getUserAuthenticationValidityDurationSeconds() != null) { + if (params.getUserAuthenticationValidityDurationSeconds() != -1) { args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, params.getUserAuthenticationValidityDurationSeconds()); } @@ -558,8 +560,8 @@ public class AndroidKeyStore extends KeyStoreSpi { args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8); if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) - || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) { - // Permit caller-specified IV. This is needed for the Cipher abstraction. + && (!params.isRandomizedEncryptionRequired())) { + // Permit caller-provided IV when encrypting with this key args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); } diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java index a59927d..635b2fa 100644 --- a/keystore/java/android/security/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/AndroidKeyStoreProvider.java @@ -49,14 +49,25 @@ public class AndroidKeyStoreProvider extends Provider { // javax.crypto.KeyGenerator put("KeyGenerator.AES", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$AES"); + put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA1"); + put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA224"); put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA256"); + put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA384"); + put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA512"); // java.security.SecretKeyFactory - put("SecretKeyFactory.AES", PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi"); - put("SecretKeyFactory.HmacSHA256", PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi"); + putSecretKeyFactoryImpl("AES"); + putSecretKeyFactoryImpl("HmacSHA1"); + putSecretKeyFactoryImpl("HmacSHA224"); + putSecretKeyFactoryImpl("HmacSHA256"); + putSecretKeyFactoryImpl("HmacSHA384"); + putSecretKeyFactoryImpl("HmacSHA512"); // javax.crypto.Mac + putMacImpl("HmacSHA224", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA224"); putMacImpl("HmacSHA256", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA256"); + putMacImpl("HmacSHA384", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA384"); + putMacImpl("HmacSHA512", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA512"); // javax.crypto.Cipher putSymmetricCipherImpl("AES/ECB/NoPadding", @@ -73,6 +84,10 @@ public class AndroidKeyStoreProvider extends Provider { PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CTR$NoPadding"); } + private void putSecretKeyFactoryImpl(String algorithm) { + put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi"); + } + private void putMacImpl(String algorithm, String implClass) { put("Mac." + algorithm, implClass); put("Mac." + algorithm + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME); diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java index 1311368..5bed2e1 100644 --- a/keystore/java/android/security/KeyGeneratorSpec.java +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -19,13 +19,10 @@ package android.security; import android.content.Context; import android.text.TextUtils; -import java.security.cert.Certificate; import java.security.spec.AlgorithmParameterSpec; -import java.util.Collections; import java.util.Date; -import java.util.HashSet; -import java.util.Set; +import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @@ -33,13 +30,13 @@ import javax.crypto.SecretKey; * {@link AlgorithmParameterSpec} for initializing a {@code KeyGenerator} that works with * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>. * - * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API - * using the {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up - * some UI to ask the user to unlock or initialize the Android KeyStore facility. + * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API using the + * {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up some UI to + * ask the user to unlock or initialize the Android KeyStore facility. * * <p>After generation, the {@code keyStoreAlias} is used with the * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)} - * interface to retrieve the {@link SecretKey} and its associated {@link Certificate} chain. + * interface to retrieve the {@link SecretKey}. * * @hide */ @@ -52,13 +49,12 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { private final Date mKeyValidityStart; private final Date mKeyValidityForOriginationEnd; private final Date mKeyValidityForConsumptionEnd; - private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; - private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; - private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; - private final Integer mMinSecondsBetweenOperations; - private final Integer mMaxUsesPerBoot; - private final Set<Integer> mUserAuthenticators; - private final Integer mUserAuthenticationValidityDurationSeconds; + private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes; + private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; + private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private final boolean mRandomizedEncryptionRequired; + private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private final int mUserAuthenticationValidityDurationSeconds; private KeyGeneratorSpec( Context context, @@ -68,19 +64,18 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { Date keyValidityStart, Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, - @KeyStoreKeyConstraints.PurposeEnum Integer purposes, - @KeyStoreKeyConstraints.PaddingEnum Integer padding, - @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode, - Integer minSecondsBetweenOperations, - Integer maxUsesPerBoot, - Set<Integer> userAuthenticators, - Integer userAuthenticationValidityDurationSeconds) { + @KeyStoreKeyConstraints.PurposeEnum int purposes, + @KeyStoreKeyConstraints.PaddingEnum int paddings, + @KeyStoreKeyConstraints.BlockModeEnum int blockModes, + boolean randomizedEncryptionRequired, + @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, + int userAuthenticationValidityDurationSeconds) { if (context == null) { throw new IllegalArgumentException("context == null"); } else if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); - } else if ((userAuthenticationValidityDurationSeconds != null) - && (userAuthenticationValidityDurationSeconds < 0)) { + } else if ((userAuthenticationValidityDurationSeconds < 0) + && (userAuthenticationValidityDurationSeconds != -1)) { throw new IllegalArgumentException( "userAuthenticationValidityDurationSeconds must not be negative"); } @@ -93,13 +88,10 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; mPurposes = purposes; - mPadding = padding; - mBlockMode = blockMode; - mMinSecondsBetweenOperations = minSecondsBetweenOperations; - mMaxUsesPerBoot = maxUsesPerBoot; - mUserAuthenticators = (userAuthenticators != null) - ? new HashSet<Integer>(userAuthenticators) - : Collections.<Integer>emptySet(); + mPaddings = paddings; + mBlockModes = blockModes; + mRandomizedEncryptionRequired = randomizedEncryptionRequired; + mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -145,8 +137,6 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * Gets the time instant after which the key is no longer valid for decryption and verification. * * @return instant or {@code null} if not restricted. - * - * @hide */ public Date getKeyValidityForConsumptionEnd() { return mKeyValidityForConsumptionEnd; @@ -163,78 +153,56 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of purposes for which the key can be used. - * - * @return set of purposes or {@code null} if the key can be used for any purpose. */ - public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() { + public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() { return mPurposes; } /** - * Gets the padding scheme to which the key is restricted. - * - * @return padding scheme or {@code null} if the padding scheme is not restricted. + * Gets the set of padding schemes to which the key is restricted. */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; + public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { + return mPaddings; } /** - * Gets the block mode to which the key is restricted when used for encryption or decryption. - * - * @return block more or {@code null} if block mode is not restricted. - * - * @hide + * Gets the set of block modes to which the key is restricted. */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; + public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { + return mBlockModes; } /** - * Gets the minimum number of seconds that must expire since the most recent use of the key - * before it can be used again. - * - * @return number of seconds or {@code null} if there is no restriction on how frequently a key - * can be used. - * - * @hide + * Returns {@code true} if encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic property + * being required is <em>indistinguishability under chosen-plaintext attack ({@code + * IND-CPA})</em>. This property is important because it mitigates several classes of + * weaknesses due to which ciphertext may leak information about plaintext. For example, if a + * given plaintext always produces the same ciphertext, an attacker may see the repeated + * ciphertexts and be able to deduce something about the plaintext. */ - public Integer getMinSecondsBetweenOperations() { - return mMinSecondsBetweenOperations; + public boolean isRandomizedEncryptionRequired() { + return mRandomizedEncryptionRequired; } /** - * Gets the number of times the key can be used without rebooting the device. + * Gets the set of user authenticators which protect access to this key. The key can only be + * used iff the user has authenticated to at least one of these user authenticators. * - * @return maximum number of times or {@code null} if there is no restriction. - * @hide + * @return user authenticators or {@code 0} if the key can be used without user authentication. */ - public Integer getMaxUsesPerBoot() { - return mMaxUsesPerBoot; - } - - /** - * Gets the user authenticators which protect access to this key. The key can only be used iff - * the user has authenticated to at least one of these user authenticators. - * - * @return user authenticators or empty set if the key can be used without user authentication. - * - * @hide - */ - public Set<Integer> getUserAuthenticators() { - return new HashSet<Integer>(mUserAuthenticators); + public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + return mUserAuthenticators; } /** * Gets the duration of time (seconds) for which this key can be used after the user * successfully authenticates to one of the associated user authenticators. * - * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication + * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication * is required for every use of the key. - * - * @hide */ - public Integer getUserAuthenticationValidityDurationSeconds() { + public int getUserAuthenticationValidityDurationSeconds() { return mUserAuthenticationValidityDurationSeconds; } @@ -253,13 +221,12 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { private Date mKeyValidityStart; private Date mKeyValidityForOriginationEnd; private Date mKeyValidityForConsumptionEnd; - private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; - private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; - private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; - private Integer mMinSecondsBetweenOperations; - private Integer mMaxUsesPerBoot; - private Set<Integer> mUserAuthenticators; - private Integer mUserAuthenticationValidityDurationSeconds; + private @KeyStoreKeyConstraints.PurposeEnum int mPurposes; + private @KeyStoreKeyConstraints.PaddingEnum int mPaddings; + private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private boolean mRandomizedEncryptionRequired = true; + private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private int mUserAuthenticationValidityDurationSeconds = -1; /** * Creates a new instance of the {@code Builder} with the given {@code context}. The @@ -318,8 +285,6 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * <b>By default, the key is valid at any instant. * * @see #setKeyValidityEnd(Date) - * - * @hide */ public Builder setKeyValidityStart(Date startDate) { mKeyValidityStart = startDate; @@ -334,8 +299,6 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * @see #setKeyValidityStart(Date) * @see #setKeyValidityForConsumptionEnd(Date) * @see #setKeyValidityForOriginationEnd(Date) - * - * @hide */ public Builder setKeyValidityEnd(Date endDate) { setKeyValidityForOriginationEnd(endDate); @@ -349,8 +312,6 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * <b>By default, the key is valid at any instant. * * @see #setKeyValidityForConsumptionEnd(Date) - * - * @hide */ public Builder setKeyValidityForOriginationEnd(Date endDate) { mKeyValidityForOriginationEnd = endDate; @@ -364,8 +325,6 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * <b>By default, the key is valid at any instant. * * @see #setKeyValidityForOriginationEnd(Date) - * - * @hide */ public Builder setKeyValidityForConsumptionEnd(Date endDate) { mKeyValidityForConsumptionEnd = endDate; @@ -373,11 +332,9 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** - * Restricts the purposes for which the key can be used to the provided set of purposes. + * Restricts the key to being used only for the provided set of purposes. * - * <p>By default, the key can be used for encryption, decryption, signing, and verification. - * - * @hide + * <p>This restriction must be specified. There is no default. */ public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) { mPurposes = purposes; @@ -385,53 +342,61 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** - * Restricts the key to being used only with the provided padding scheme. Attempts to use + * Restricts the key to being used only with the provided padding schemes. Attempts to use * the key with any other padding will be rejected. * * <p>This restriction must be specified for keys which are used for encryption/decryption. - * - * @hide */ - public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) { - mPadding = padding; + public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) { + mPaddings = paddings; return this; } /** - * Restricts the key to being used only with the provided block mode when encrypting or - * decrypting. Attempts to use the key with any other block modes will be rejected. + * Restricts the key to being used only with the provided block modes. Attempts to use the + * key with any other block modes will be rejected. * * <p>This restriction must be specified for keys which are used for encryption/decryption. - * - * @hide */ - public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) { - mBlockMode = blockMode; + public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) { + mBlockModes = blockModes; return this; } /** - * Sets the minimum number of seconds that must expire since the most recent use of the key - * before it can be used again. - * - * <p>By default, there is no restriction on how frequently a key can be used. - * - * @hide + * Sets whether encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic + * property being required is <em>indistinguishability under chosen-plaintext attack + * ({@code IND-CPA})</em>. This property is important because it mitigates several classes + * of weaknesses due to which ciphertext may leak information about plaintext. For example, + * if a given plaintext always produces the same ciphertext, an attacker may see the + * repeated ciphertexts and be able to deduce something about the plaintext. + * + * <p>By default, {@code IND-CPA} is required. + * + * <p>When {@code IND-CPA} is required: + * <ul> + * <li>block modes which do not offer {@code IND-CPA}, such as {@code ECB}, are prohibited; + * </li> + * <li>in block modes which use an IV, such as {@code CBC}, {@code CTR}, and {@code GCM}, + * caller-provided IVs are rejected when encrypting, to ensure that only random IVs are + * used.</li> + * + * <p>Before disabling this requirement, consider the following approaches instead: + * <ul> + * <li>If you are generating a random IV for encryption and then initializing a {@code} + * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV + * instead. This will occur if the {@code Cipher} is initialized for encryption without an + * IV. The IV can then be queried via {@link Cipher#getIV()}.</li> + * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully + * random, such as the name of the file being encrypted, or transaction ID, or password, + * or a device identifier), consider changing your design to use a random IV which will then + * be provided in addition to the ciphertext to the entities which need to decrypt the + * ciphertext.</li> + * </ul> */ - public Builder setMinSecondsBetweenOperations(int seconds) { - mMinSecondsBetweenOperations = seconds; - return this; - } - - /** - * Sets the maximum number of times a key can be used without rebooting the device. - * - * <p>By default, the key can be used for an unlimited number of times. - * - * @hide - */ - public Builder setMaxUsesPerBoot(int count) { - mMaxUsesPerBoot = count; + public Builder setRandomizedEncryptionRequired(boolean required) { + mRandomizedEncryptionRequired = required; return this; } @@ -445,12 +410,10 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * without user authentication. * * @see #setUserAuthenticationValidityDurationSeconds(int) - * - * @hide */ - public Builder setUserAuthenticators(Set<Integer> userAuthenticators) { - mUserAuthenticators = - (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null; + public Builder setUserAuthenticators( + @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) { + mUserAuthenticators = userAuthenticators; return this; } @@ -463,9 +426,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for * every use of the key. * - * @see #setUserAuthenticators(Set) - * - * @hide + * @see #setUserAuthenticators(int) */ public Builder setUserAuthenticationValidityDurationSeconds(int seconds) { mUserAuthenticationValidityDurationSeconds = seconds; @@ -478,10 +439,19 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * @throws IllegalArgumentException if a required field is missing or violates a constraint. */ public KeyGeneratorSpec build() { - return new KeyGeneratorSpec(mContext, mKeystoreAlias, mFlags, mKeySize, - mKeyValidityStart, mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, - mPurposes, mPadding, mBlockMode, mMinSecondsBetweenOperations, mMaxUsesPerBoot, - mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); + return new KeyGeneratorSpec(mContext, + mKeystoreAlias, + mFlags, + mKeySize, + mKeyValidityStart, + mKeyValidityForOriginationEnd, + mKeyValidityForConsumptionEnd, + mPurposes, + mPaddings, + mBlockModes, + mRandomizedEncryptionRequired, + mUserAuthenticators, + mUserAuthenticationValidityDurationSeconds); } } } diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index 0001604..8945701 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -24,10 +24,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.spec.AlgorithmParameterSpec; -import java.util.Collections; import java.util.Date; -import java.util.HashSet; -import java.util.Set; import javax.security.auth.x500.X500Principal; @@ -81,21 +78,19 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private final Date mKeyValidityForConsumptionEnd; - private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; + private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes; - private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest; + private final @KeyStoreKeyConstraints.DigestEnum int mDigests; - private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; + private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; - private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; + private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; - private final Integer mMinSecondsBetweenOperations; + private final boolean mRandomizedEncryptionRequired; - private final Integer mMaxUsesPerBoot; + private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; - private final Set<Integer> mUserAuthenticators; - - private final Integer mUserAuthenticationValidityDurationSeconds; + private final int mUserAuthenticationValidityDurationSeconds; /** * Parameter specification for the "{@code AndroidKeyPairGenerator}" @@ -135,14 +130,13 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { Date keyValidityStart, Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, - @KeyStoreKeyConstraints.PurposeEnum Integer purposes, - @KeyStoreKeyConstraints.DigestEnum Integer digest, - @KeyStoreKeyConstraints.PaddingEnum Integer padding, - @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode, - Integer minSecondsBetweenOperations, - Integer maxUsesPerBoot, - Set<Integer> userAuthenticators, - Integer userAuthenticationValidityDurationSeconds) { + @KeyStoreKeyConstraints.PurposeEnum int purposes, + @KeyStoreKeyConstraints.DigestEnum int digests, + @KeyStoreKeyConstraints.PaddingEnum int paddings, + @KeyStoreKeyConstraints.BlockModeEnum int blockModes, + boolean randomizedEncryptionRequired, + @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, + int userAuthenticationValidityDurationSeconds) { if (context == null) { throw new IllegalArgumentException("context == null"); } else if (TextUtils.isEmpty(keyStoreAlias)) { @@ -157,8 +151,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { throw new IllegalArgumentException("endDate == null"); } else if (endDate.before(startDate)) { throw new IllegalArgumentException("endDate < startDate"); - } else if ((userAuthenticationValidityDurationSeconds != null) - && (userAuthenticationValidityDurationSeconds < 0)) { + } else if ((userAuthenticationValidityDurationSeconds < 0) + && (userAuthenticationValidityDurationSeconds != -1)) { throw new IllegalArgumentException( "userAuthenticationValidityDurationSeconds must not be negative"); } @@ -177,14 +171,11 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; mPurposes = purposes; - mDigest = digest; - mPadding = padding; - mBlockMode = blockMode; - mMinSecondsBetweenOperations = minSecondsBetweenOperations; - mMaxUsesPerBoot = maxUsesPerBoot; - mUserAuthenticators = (userAuthenticators != null) - ? new HashSet<Integer>(userAuthenticators) - : Collections.<Integer>emptySet(); + mDigests = digests; + mPaddings = paddings; + mBlockModes = blockModes; + mRandomizedEncryptionRequired = randomizedEncryptionRequired; + mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -195,9 +186,26 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize, AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate, int flags) { - this(context, keyStoreAlias, keyType, keySize, spec, subjectDN, serialNumber, startDate, - endDate, flags, startDate, endDate, endDate, null, null, null, null, null, null, - null, null); + this(context, + keyStoreAlias, + keyType, + keySize, + spec, + subjectDN, + serialNumber, + startDate, + endDate, + flags, + startDate, + endDate, + endDate, + 0, + 0, + 0, + 0, + true, + 0, + -1); } /** @@ -323,90 +331,67 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of purposes for which the key can be used. * - * @return set of purposes or {@code null} if the key can be used for any purpose. - * * @hide */ - public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() { + public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() { return mPurposes; } /** - * Gets the digest to which the key is restricted. - * - * @return digest or {@code null} if the digest is not restricted. - * - * @hide - */ - public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() { - return mDigest; - } - - /** - * Gets the padding scheme to which the key is restricted. - * - * @return padding scheme or {@code null} if the padding scheme is not restricted. + * Gets the set of digests to which the key is restricted. * * @hide */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; + public @KeyStoreKeyConstraints.DigestEnum int getDigests() { + return mDigests; } /** - * Gets the block mode to which the key is restricted when used for encryption or decryption. - * - * @return block more or {@code null} if block mode is not restricted. + * Gets the set of padding schemes to which the key is restricted. * * @hide */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; + public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { + return mPaddings; } /** - * Gets the minimum number of seconds that must expire since the most recent use of the private - * key before it can be used again. - * - * <p>This restriction applies only to private key operations. Public key operations are not - * restricted. - * - * @return number of seconds or {@code null} if there is no restriction on how frequently a key - * can be used. + * Gets the set of block modes to which the key is restricted. * * @hide */ - public Integer getMinSecondsBetweenOperations() { - return mMinSecondsBetweenOperations; + public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { + return mBlockModes; } /** - * Gets the number of times the private key can be used without rebooting the device. - * - * <p>This restriction applies only to private key operations. Public key operations are not - * restricted. - * - * @return maximum number of times or {@code null} if there is no restriction. + * Returns {@code true} if encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic property + * being required is <em>indistinguishability under chosen-plaintext attack ({@code + * IND-CPA})</em>. This property is important because it mitigates several classes of + * weaknesses due to which ciphertext may leak information about plaintext. For example, if a + * given plaintext always produces the same ciphertext, an attacker may see the repeated + * ciphertexts and be able to deduce something about the plaintext. * * @hide */ - public Integer getMaxUsesPerBoot() { - return mMaxUsesPerBoot; + public boolean isRandomizedEncryptionRequired() { + return mRandomizedEncryptionRequired; } /** - * Gets the user authenticators which protect access to the private key. The key can only be - * used iff the user has authenticated to at least one of these user authenticators. + * Gets the set of user authenticators which protect access to the private key. The key can only + * be used iff the user has authenticated to at least one of these user authenticators. * * <p>This restriction applies only to private key operations. Public key operations are not * restricted. * - * @return user authenticators or empty set if the key can be used without user authentication. + * @return user authenticators or {@code 0} if the key can be used without user authentication. * * @hide */ - public Set<Integer> getUserAuthenticators() { - return new HashSet<Integer>(mUserAuthenticators); + public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + return mUserAuthenticators; } /** @@ -416,12 +401,12 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * <p>This restriction applies only to private key operations. Public key operations are not * restricted. * - * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication + * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication * is required for every use of the key. * * @hide */ - public Integer getUserAuthenticationValidityDurationSeconds() { + public int getUserAuthenticationValidityDurationSeconds() { return mUserAuthenticationValidityDurationSeconds; } @@ -473,21 +458,19 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private Date mKeyValidityForConsumptionEnd; - private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; - - private @KeyStoreKeyConstraints.DigestEnum Integer mDigest; + private @KeyStoreKeyConstraints.PurposeEnum int mPurposes; - private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; + private @KeyStoreKeyConstraints.DigestEnum int mDigests; - private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; + private @KeyStoreKeyConstraints.PaddingEnum int mPaddings; - private Integer mMinSecondsBetweenOperations; + private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; - private Integer mMaxUsesPerBoot; + private boolean mRandomizedEncryptionRequired = true; - private Set<Integer> mUserAuthenticators; + private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; - private Integer mUserAuthenticationValidityDurationSeconds; + private int mUserAuthenticationValidityDurationSeconds = -1; /** * Creates a new instance of the {@code Builder} with the given @@ -675,9 +658,9 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** - * Restricts the purposes for which the key can be used to the provided set of purposes. + * Restricts the key to being used only for the provided set of purposes. * - * <p>By default, the key can be used for encryption, decryption, signing, and verification. + * <p>This restriction must be specified. There is no default. * * @hide */ @@ -687,28 +670,28 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** - * Restricts the key to being used only with the provided digest. Attempts to use the key + * Restricts the key to being used only with the provided digests. Attempts to use the key * with any other digests be rejected. * * <p>This restriction must be specified for keys which are used for signing/verification. * * @hide */ - public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) { - mDigest = digest; + public Builder setDigests(@KeyStoreKeyConstraints.DigestEnum int digests) { + mDigests = digests; return this; } /** - * Restricts the key to being used only with the provided padding scheme. Attempts to use + * Restricts the key to being used only with the provided padding schemes. Attempts to use * the key with any other padding will be rejected. * * <p>This restriction must be specified for keys which are used for encryption/decryption. * * @hide */ - public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) { - mPadding = padding; + public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) { + mPaddings = paddings; return this; } @@ -720,39 +703,35 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @hide */ - public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) { - mBlockMode = blockMode; + public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) { + mBlockModes = blockModes; return this; } /** - * Sets the minimum number of seconds that must expire since the most recent use of the key - * before it can be used again. - * - * <p>By default, there is no restriction on how frequently a key can be used. - * - * <p>This restriction applies only to private key operations. Public key operations are not - * restricted. + * Sets whether encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic + * property being required is <em>indistinguishability under chosen-plaintext attack + * ({@code IND-CPA})</em>. This property is important because it mitigates several classes + * of weaknesses due to which ciphertext may leak information about plaintext. For example, + * if a given plaintext always produces the same ciphertext, an attacker may see the + * repeated ciphertexts and be able to deduce something about the plaintext. * - * @hide - */ - public Builder setMinSecondsBetweenOperations(int seconds) { - mMinSecondsBetweenOperations = seconds; - return this; - } - - /** - * Sets the maximum number of times a key can be used without rebooting the device. + * <p>By default, {@code IND-CPA} is required. * - * <p>By default, the key can be used for an unlimited number of times. + * <p>When {@code IND-CPA} is required, encryption/decryption transformations which do not + * offer {@code IND-CPA}, such as RSA without padding, are prohibited. * - * <p>This restriction applies only to private key operations. Public key operations are not - * restricted. + * <p>Before disabling this requirement, consider the following approaches instead: + * <ul> + * <li>If you are using RSA encryption without padding, consider switching to padding + * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li> + * </ul> * * @hide */ - public Builder setMaxUsesPerBoot(int count) { - mMaxUsesPerBoot = count; + public Builder setRandomizedEncryptionRequired(boolean required) { + mRandomizedEncryptionRequired = required; return this; } @@ -765,16 +744,16 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * <p>This restriction applies only to private key operations. Public key operations are not * restricted. * - * @param userAuthenticators user authenticators or empty list if this key can be accessed + * @param userAuthenticators user authenticators or {@code 0} if this key can be accessed * without user authentication. * * @see #setUserAuthenticationValidityDurationSeconds(int) * * @hide */ - public Builder setUserAuthenticators(Set<Integer> userAuthenticators) { - mUserAuthenticators = - (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null; + public Builder setUserAuthenticators( + @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) { + mUserAuthenticators = userAuthenticators; return this; } @@ -790,7 +769,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for * every use of the key. * - * @see #setUserAuthenticators(Set) + * @see #setUserAuthenticators(int) * * @hide */ @@ -820,11 +799,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, - mDigest, - mPadding, - mBlockMode, - mMinSecondsBetweenOperations, - mMaxUsesPerBoot, + mDigests, + mPaddings, + mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); } diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java index ec358d6..487eac0 100644 --- a/keystore/java/android/security/KeyStoreCipherSpi.java +++ b/keystore/java/android/security/KeyStoreCipherSpi.java @@ -111,7 +111,9 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockMode; private final @KeyStoreKeyConstraints.PaddingEnum int mPadding; private final int mBlockSizeBytes; - private final boolean mIvUsed; + + /** Whether this transformation requires an IV. */ + private final boolean mIvRequired; // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after // doFinal finishes. @@ -119,10 +121,13 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry private KeyStoreSecretKey mKey; private SecureRandom mRng; private boolean mFirstOperationInitiated; - byte[] mIv; + private byte[] mIv; + /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */ + private boolean mIvHasBeenUsed; - // Fields below must be reset + // Fields below must be reset after doFinal private byte[] mAdditionalEntropyForBegin; + /** * Token referencing this operation inside keystore service. It is initialized by * {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and one some @@ -143,7 +148,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry mBlockMode = blockMode; mPadding = padding; mBlockSizeBytes = blockSizeBytes; - mIvUsed = ivUsed; + mIvRequired = ivUsed; } @Override @@ -170,7 +175,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry } private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { - reset(); + resetAll(); if (!(key instanceof KeyStoreSecretKey)) { throw new InvalidKeyException( "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null")); @@ -187,7 +192,25 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry mEncrypting = opmode == Cipher.ENCRYPT_MODE; } - private void reset() { + private void resetAll() { + IBinder operationToken = mOperationToken; + if (operationToken != null) { + mOperationToken = null; + mKeyStore.abort(operationToken); + } + mEncrypting = false; + mKey = null; + mRng = null; + mFirstOperationInitiated = false; + mIv = null; + mIvHasBeenUsed = false; + mAdditionalEntropyForBegin = null; + mOperationToken = null; + mOperationHandle = null; + mMainDataStreamer = null; + } + + private void resetWhilePreservingInitState() { IBinder operationToken = mOperationToken; if (operationToken != null) { mOperationToken = null; @@ -205,6 +228,12 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry if (mKey == null) { throw new IllegalStateException("Not initialized"); } + if ((mEncrypting) && (mIvRequired) && (mIvHasBeenUsed)) { + // IV is being reused for encryption: this violates security best practices. + throw new IllegalStateException( + "IV has already been used. Reusing IV in encryption mode violates security best" + + " practices."); + } KeymasterArguments keymasterInputArgs = new KeymasterArguments(); keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mAlgorithm); @@ -234,6 +263,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry mOperationHandle = opResult.operationHandle; loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs); mFirstOperationInitiated = true; + mIvHasBeenUsed = true; mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer( new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( mKeyStore, opResult.token)); @@ -298,7 +328,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry } } - reset(); + resetWhilePreservingInitState(); return output; } @@ -376,7 +406,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry */ @Override protected AlgorithmParameters engineGetParameters() { - if (!mIvUsed) { + if (!mIvRequired) { return null; } if ((mIv != null) && (mIv.length > 0)) { @@ -408,7 +438,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry */ protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { - if (!mIvUsed) { + if (!mIvRequired) { if (params != null) { throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); } @@ -447,7 +477,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry */ protected void initAlgorithmSpecificParameters(AlgorithmParameters params) throws InvalidAlgorithmParameterException { - if (!mIvUsed) { + if (!mIvRequired) { if (params != null) { throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); } @@ -492,7 +522,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry * and thus {@code Cipher.init} needs to be invoked with explicitly provided parameters. */ protected void initAlgorithmSpecificParameters() throws InvalidKeyException { - if (!mIvUsed) { + if (!mIvRequired) { return; } @@ -515,7 +545,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry if (!mFirstOperationInitiated) { // First begin operation -- see if we need to provide additional entropy for IV // generation. - if (mIvUsed) { + if (mIvRequired) { // IV is needed if ((mIv == null) && (mEncrypting)) { // TODO: Switch to keymaster-generated IV code below once keymaster supports @@ -534,7 +564,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry } } - if ((mIvUsed) && (mIv != null)) { + if ((mIvRequired) && (mIv != null)) { keymasterArgs.addBlob(KeymasterDefs.KM_TAG_NONCE, mIv); } } @@ -557,7 +587,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry returnedIv = null; } - if (mIvUsed) { + if (mIvRequired) { if (mIv == null) { mIv = returnedIv; } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) { diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java index a5864a4..f69e7d1 100644 --- a/keystore/java/android/security/KeyStoreHmacSpi.java +++ b/keystore/java/android/security/KeyStoreHmacSpi.java @@ -35,9 +35,33 @@ import javax.crypto.MacSpi; */ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation { + public static class HmacSHA1 extends KeyStoreHmacSpi { + public HmacSHA1() { + super(KeyStoreKeyConstraints.Digest.SHA1); + } + } + + public static class HmacSHA224 extends KeyStoreHmacSpi { + public HmacSHA224() { + super(KeyStoreKeyConstraints.Digest.SHA224); + } + } + public static class HmacSHA256 extends KeyStoreHmacSpi { public HmacSHA256() { - super(KeyStoreKeyConstraints.Digest.SHA256, 256 / 8); + super(KeyStoreKeyConstraints.Digest.SHA256); + } + } + + public static class HmacSHA384 extends KeyStoreHmacSpi { + public HmacSHA384() { + super(KeyStoreKeyConstraints.Digest.SHA384); + } + } + + public static class HmacSHA512 extends KeyStoreHmacSpi { + public HmacSHA512() { + super(KeyStoreKeyConstraints.Digest.SHA512); } } @@ -52,9 +76,9 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp private IBinder mOperationToken; private Long mOperationHandle; - protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest, int macSizeBytes) { + protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest) { mDigest = digest; - mMacSizeBytes = macSizeBytes; + mMacSizeBytes = KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest); } @Override diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java index c27ccb1..7f691fb 100644 --- a/keystore/java/android/security/KeyStoreKeyConstraints.java +++ b/keystore/java/android/security/KeyStoreKeyConstraints.java @@ -21,12 +21,8 @@ import android.security.keymaster.KeymasterDefs; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.Locale; -import java.util.Set; /** * Constraints for {@code AndroidKeyStore} keys. @@ -37,7 +33,8 @@ public abstract class KeyStoreKeyConstraints { private KeyStoreKeyConstraints() {} @Retention(RetentionPolicy.SOURCE) - @IntDef(flag=true, value={Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY}) + @IntDef(flag = true, + value = {Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY}) public @interface PurposeEnum {} /** @@ -67,11 +64,6 @@ public abstract class KeyStoreKeyConstraints { public static final int VERIFY = 1 << 3; /** - * Number of flags defined above. Needs to be kept in sync with the flags above. - */ - private static final int VALUE_COUNT = 4; - - /** * @hide */ public static int toKeymaster(@PurposeEnum int purpose) { @@ -110,22 +102,12 @@ public abstract class KeyStoreKeyConstraints { /** * @hide */ - public static int[] allToKeymaster(int purposes) { - int[] result = new int[VALUE_COUNT]; - int resultCount = 0; - int purpose = 1; - for (int i = 0; i < 32; i++) { - if ((purposes & 1) != 0) { - result[resultCount] = toKeymaster(purpose); - resultCount++; - } - purposes >>>= 1; - purpose <<= 1; - if (purposes == 0) { - break; - } + public static int[] allToKeymaster(@PurposeEnum int purposes) { + int[] result = getSetFlags(purposes); + for (int i = 0; i < result.length; i++) { + result[i] = toKeymaster(result[i]); } - return Arrays.copyOf(result, resultCount); + return result; } /** @@ -244,7 +226,8 @@ public abstract class KeyStoreKeyConstraints { } @Retention(RetentionPolicy.SOURCE) - @IntDef({Padding.NONE, Padding.ZERO, Padding.PKCS7}) + @IntDef(flag = true, + value = {Padding.NONE, Padding.PKCS7}) public @interface PaddingEnum {} /** @@ -256,17 +239,12 @@ public abstract class KeyStoreKeyConstraints { /** * No padding. */ - public static final int NONE = 0; - - /** - * Pad with zeros. - */ - public static final int ZERO = 1; + public static final int NONE = 1 << 0; /** * PKCS#7 padding. */ - public static final int PKCS7 = 2; + public static final int PKCS7 = 1 << 1; /** * @hide @@ -275,8 +253,6 @@ public abstract class KeyStoreKeyConstraints { switch (padding) { case NONE: return KeymasterDefs.KM_PAD_NONE; - case ZERO: - return KeymasterDefs.KM_PAD_ZERO; case PKCS7: return KeymasterDefs.KM_PAD_PKCS7; default: @@ -291,8 +267,6 @@ public abstract class KeyStoreKeyConstraints { switch (padding) { case KeymasterDefs.KM_PAD_NONE: return NONE; - case KeymasterDefs.KM_PAD_ZERO: - return ZERO; case KeymasterDefs.KM_PAD_PKCS7: return PKCS7; default: @@ -307,8 +281,6 @@ public abstract class KeyStoreKeyConstraints { switch (padding) { case NONE: return "NONE"; - case ZERO: - return "ZERO"; case PKCS7: return "PKCS#7"; default: @@ -329,10 +301,41 @@ public abstract class KeyStoreKeyConstraints { throw new IllegalArgumentException("Unknown padding: " + padding); } } + + /** + * @hide + */ + public static int[] allToKeymaster(@PaddingEnum int paddings) { + int[] result = getSetFlags(paddings); + for (int i = 0; i < result.length; i++) { + result[i] = toKeymaster(result[i]); + } + return result; + } + + /** + * @hide + */ + public static @PaddingEnum int allFromKeymaster(Collection<Integer> paddings) { + @PaddingEnum int result = 0; + for (int keymasterPadding : paddings) { + result |= fromKeymaster(keymasterPadding); + } + return result; + } } @Retention(RetentionPolicy.SOURCE) - @IntDef({Digest.NONE, Digest.SHA256}) + @IntDef(flag = true, + value = { + Digest.NONE, + Digest.MD5, + Digest.SHA1, + Digest.SHA224, + Digest.SHA256, + Digest.SHA384, + Digest.SHA512, + }) public @interface DigestEnum {} /** @@ -345,12 +348,37 @@ public abstract class KeyStoreKeyConstraints { /** * No digest: sign/authenticate the raw message. */ - public static final int NONE = 0; + public static final int NONE = 1 << 0; /** - * SHA-256 digest. + * MD5 digest. */ - public static final int SHA256 = 1; + public static final int MD5 = 1 << 1; + + /** + * SHA-1 digest. + */ + public static final int SHA1 = 1 << 2; + + /** + * SHA-2 224 (aka SHA-224) digest. + */ + public static final int SHA224 = 1 << 3; + + /** + * SHA-2 256 (aka SHA-256) digest. + */ + public static final int SHA256 = 1 << 4; + + /** + * SHA-2 384 (aka SHA-384) digest. + */ + public static final int SHA384 = 1 << 5; + + /** + * SHA-2 512 (aka SHA-512) digest. + */ + public static final int SHA512 = 1 << 6; /** * @hide @@ -359,8 +387,18 @@ public abstract class KeyStoreKeyConstraints { switch (digest) { case NONE: return "NONE"; + case MD5: + return "MD5"; + case SHA1: + return "SHA-1"; + case SHA224: + return "SHA-224"; case SHA256: - return "SHA256"; + return "SHA-256"; + case SHA384: + return "SHA-384"; + case SHA512: + return "SHA-512"; default: throw new IllegalArgumentException("Unknown digest: " + digest); } @@ -369,12 +407,40 @@ public abstract class KeyStoreKeyConstraints { /** * @hide */ + public static String allToString(@DigestEnum int digests) { + StringBuilder result = new StringBuilder("["); + boolean firstValue = true; + for (@DigestEnum int digest : getSetFlags(digests)) { + if (firstValue) { + firstValue = false; + } else { + result.append(", "); + } + result.append(toString(digest)); + } + result.append(']'); + return result.toString(); + } + + /** + * @hide + */ public static int toKeymaster(@DigestEnum int digest) { switch (digest) { case NONE: return KeymasterDefs.KM_DIGEST_NONE; + case MD5: + return KeymasterDefs.KM_DIGEST_MD5; + case SHA1: + return KeymasterDefs.KM_DIGEST_SHA1; + case SHA224: + return KeymasterDefs.KM_DIGEST_SHA_2_224; case SHA256: return KeymasterDefs.KM_DIGEST_SHA_2_256; + case SHA384: + return KeymasterDefs.KM_DIGEST_SHA_2_384; + case SHA512: + return KeymasterDefs.KM_DIGEST_SHA_2_512; default: throw new IllegalArgumentException("Unknown digest: " + digest); } @@ -387,8 +453,18 @@ public abstract class KeyStoreKeyConstraints { switch (digest) { case KeymasterDefs.KM_DIGEST_NONE: return NONE; + case KeymasterDefs.KM_DIGEST_MD5: + return MD5; + case KeymasterDefs.KM_DIGEST_SHA1: + return SHA1; + case KeymasterDefs.KM_DIGEST_SHA_2_224: + return SHA224; case KeymasterDefs.KM_DIGEST_SHA_2_256: return SHA256; + case KeymasterDefs.KM_DIGEST_SHA_2_384: + return SHA384; + case KeymasterDefs.KM_DIGEST_SHA_2_512: + return SHA512; default: throw new IllegalArgumentException("Unknown digest: " + digest); } @@ -397,14 +473,46 @@ public abstract class KeyStoreKeyConstraints { /** * @hide */ + public static int[] allToKeymaster(@DigestEnum int digests) { + int[] result = getSetFlags(digests); + for (int i = 0; i < result.length; i++) { + result[i] = toKeymaster(result[i]); + } + return result; + } + + /** + * @hide + */ + public static @DigestEnum int allFromKeymaster(Collection<Integer> digests) { + @DigestEnum int result = 0; + for (int keymasterDigest : digests) { + result |= fromKeymaster(keymasterDigest); + } + return result; + } + + /** + * @hide + */ public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) { String algorithmLower = algorithm.toLowerCase(Locale.US); if (algorithmLower.startsWith("hmac")) { - if ("hmacsha256".equals(algorithmLower)) { + String digestLower = algorithmLower.substring("hmac".length()); + if ("md5".equals(digestLower)) { + return MD5; + } else if ("sha1".equals(digestLower)) { + return SHA1; + } else if ("sha224".equals(digestLower)) { + return SHA224; + } else if ("sha256".equals(digestLower)) { return SHA256; + } else if ("sha384".equals(digestLower)) { + return SHA384; + } else if ("sha512".equals(digestLower)) { + return SHA512; } else { - throw new IllegalArgumentException("Unsupported digest: " - + algorithmLower.substring("hmac".length())); + throw new IllegalArgumentException("Unsupported digest: " + digestLower); } } else { return null; @@ -418,8 +526,18 @@ public abstract class KeyStoreKeyConstraints { switch (digest) { case NONE: return "NONE"; + case MD5: + return "MD5"; + case SHA1: + return "SHA1"; + case SHA224: + return "SHA224"; case SHA256: return "SHA256"; + case SHA384: + return "SHA384"; + case SHA512: + return "SHA512"; default: throw new IllegalArgumentException("Unknown digest: " + digest); } @@ -432,8 +550,18 @@ public abstract class KeyStoreKeyConstraints { switch (digest) { case NONE: return null; + case MD5: + return 128 / 8; + case SHA1: + return 160 / 8; + case SHA224: + return 224 / 8; case SHA256: return 256 / 8; + case SHA384: + return 384 / 8; + case SHA512: + return 512 / 8; default: throw new IllegalArgumentException("Unknown digest: " + digest); } @@ -441,7 +569,8 @@ public abstract class KeyStoreKeyConstraints { } @Retention(RetentionPolicy.SOURCE) - @IntDef({BlockMode.ECB, BlockMode.CBC, BlockMode.CTR}) + @IntDef(flag = true, + value = {BlockMode.ECB, BlockMode.CBC, BlockMode.CTR, BlockMode.GCM}) public @interface BlockModeEnum {} /** @@ -451,13 +580,24 @@ public abstract class KeyStoreKeyConstraints { private BlockMode() {} /** Electronic Codebook (ECB) block mode. */ - public static final int ECB = 0; + public static final int ECB = 1 << 0; /** Cipher Block Chaining (CBC) block mode. */ - public static final int CBC = 1; + public static final int CBC = 1 << 1; /** Counter (CTR) block mode. */ - public static final int CTR = 2; + public static final int CTR = 1 << 2; + + /** Galois/Counter Mode (GCM) block mode. */ + public static final int GCM = 1 << 3; + + /** + * Set of block modes compatible with IND-CPA if used correctly. + * + * @hide + */ + public static final @BlockModeEnum int IND_CPA_COMPATIBLE_MODES = + CBC | CTR | GCM; /** * @hide @@ -470,6 +610,8 @@ public abstract class KeyStoreKeyConstraints { return KeymasterDefs.KM_MODE_CBC; case CTR: return KeymasterDefs.KM_MODE_CTR; + case GCM: + return KeymasterDefs.KM_MODE_GCM; default: throw new IllegalArgumentException("Unknown block mode: " + mode); } @@ -486,6 +628,8 @@ public abstract class KeyStoreKeyConstraints { return CBC; case KeymasterDefs.KM_MODE_CTR: return CTR; + case KeymasterDefs.KM_MODE_GCM: + return GCM; default: throw new IllegalArgumentException("Unknown block mode: " + mode); } @@ -494,6 +638,28 @@ public abstract class KeyStoreKeyConstraints { /** * @hide */ + public static int[] allToKeymaster(@BlockModeEnum int modes) { + int[] result = getSetFlags(modes); + for (int i = 0; i < result.length; i++) { + result[i] = toKeymaster(result[i]); + } + return result; + } + + /** + * @hide + */ + public static @BlockModeEnum int allFromKeymaster(Collection<Integer> modes) { + @BlockModeEnum int result = 0; + for (int keymasterMode : modes) { + result |= fromKeymaster(keymasterMode); + } + return result; + } + + /** + * @hide + */ public static String toString(@BlockModeEnum int mode) { switch (mode) { case ECB: @@ -502,6 +668,8 @@ public abstract class KeyStoreKeyConstraints { return "CBC"; case CTR: return "CTR"; + case GCM: + return "GCM"; default: throw new IllegalArgumentException("Unknown block mode: " + mode); } @@ -510,6 +678,24 @@ public abstract class KeyStoreKeyConstraints { /** * @hide */ + public static String allToString(@BlockModeEnum int modes) { + StringBuilder result = new StringBuilder("["); + boolean firstValue = true; + for (@BlockModeEnum int mode : getSetFlags(modes)) { + if (firstValue) { + firstValue = false; + } else { + result.append(", "); + } + result.append(toString(mode)); + } + result.append(']'); + return result.toString(); + } + + /** + * @hide + */ public static @BlockModeEnum int fromJCAMode(String mode) { String modeLower = mode.toLowerCase(Locale.US); if ("ecb".equals(modeLower)) { @@ -518,6 +704,8 @@ public abstract class KeyStoreKeyConstraints { return CBC; } else if ("ctr".equals(modeLower)) { return CTR; + } else if ("gcm".equals(modeLower)) { + return GCM; } else { throw new IllegalArgumentException("Unknown block mode: " + mode); } @@ -525,7 +713,8 @@ public abstract class KeyStoreKeyConstraints { } @Retention(RetentionPolicy.SOURCE) - @IntDef({UserAuthenticator.LOCK_SCREEN}) + @IntDef(flag = true, + value = {UserAuthenticator.LOCK_SCREEN}) public @interface UserAuthenticatorEnum {} /** @@ -535,7 +724,7 @@ public abstract class KeyStoreKeyConstraints { private UserAuthenticator() {} /** Lock screen. */ - public static final int LOCK_SCREEN = 1; + public static final int LOCK_SCREEN = 1 << 0; /** * @hide @@ -543,7 +732,7 @@ public abstract class KeyStoreKeyConstraints { public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) { switch (userAuthenticator) { case LOCK_SCREEN: - return LOCK_SCREEN; + return KeymasterDefs.HW_AUTH_PASSWORD; default: throw new IllegalArgumentException( "Unknown user authenticator: " + userAuthenticator); @@ -555,7 +744,7 @@ public abstract class KeyStoreKeyConstraints { */ public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) { switch (userAuthenticator) { - case LOCK_SCREEN: + case KeymasterDefs.HW_AUTH_PASSWORD: return LOCK_SCREEN; default: throw new IllegalArgumentException( @@ -566,10 +755,15 @@ public abstract class KeyStoreKeyConstraints { /** * @hide */ - public static int allToKeymaster(Set<Integer> userAuthenticators) { + public static int allToKeymaster(@UserAuthenticatorEnum int userAuthenticators) { int result = 0; - for (@UserAuthenticatorEnum int userAuthenticator : userAuthenticators) { - result |= toKeymaster(userAuthenticator); + int userAuthenticator = 1; + while (userAuthenticators != 0) { + if ((userAuthenticators & 1) != 0) { + result |= toKeymaster(userAuthenticator); + } + userAuthenticators >>>= 1; + userAuthenticator <<= 1; } return result; } @@ -577,20 +771,17 @@ public abstract class KeyStoreKeyConstraints { /** * @hide */ - public static Set<Integer> allFromKeymaster(int userAuthenticators) { + public static @UserAuthenticatorEnum int allFromKeymaster(int userAuthenticators) { + @UserAuthenticatorEnum int result = 0; int userAuthenticator = 1; - Set<Integer> result = null; while (userAuthenticators != 0) { if ((userAuthenticators & 1) != 0) { - if (result == null) { - result = new HashSet<Integer>(); - } - result.add(fromKeymaster(userAuthenticator)); + result |= fromKeymaster(userAuthenticator); } userAuthenticators >>>= 1; userAuthenticator <<= 1; } - return (result != null) ? result : Collections.<Integer>emptySet(); + return result; } /** @@ -606,4 +797,38 @@ public abstract class KeyStoreKeyConstraints { } } } + + private static final int[] EMPTY_INT_ARRAY = new int[0]; + + private static int[] getSetFlags(int flags) { + if (flags == 0) { + return EMPTY_INT_ARRAY; + } + int result[] = new int[getSetBitCount(flags)]; + int resultOffset = 0; + int flag = 1; + while (flags != 0) { + if ((flags & 1) != 0) { + result[resultOffset] = flag; + resultOffset++; + } + flags >>>= 1; + flag <<= 1; + } + return result; + } + + private static int getSetBitCount(int value) { + if (value == 0) { + return 0; + } + int result = 0; + while (value != 0) { + if ((value & 1) != 0) { + result++; + } + value >>>= 1; + } + return result; + } } diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java index c9c9bd8..a500786 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -41,12 +41,41 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } } - public static class HmacSHA256 extends KeyStoreKeyGeneratorSpi { - public HmacSHA256() { + protected static abstract class HmacBase extends KeyStoreKeyGeneratorSpi { + protected HmacBase(@KeyStoreKeyConstraints.DigestEnum int digest) { super(KeyStoreKeyConstraints.Algorithm.HMAC, - KeyStoreKeyConstraints.Digest.SHA256, - KeyStoreKeyConstraints.Digest.getOutputSizeBytes( - KeyStoreKeyConstraints.Digest.SHA256) * 8); + digest, + KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest) * 8); + } + } + + public static class HmacSHA1 extends HmacBase { + public HmacSHA1() { + super(KeyStoreKeyConstraints.Digest.SHA1); + } + } + + public static class HmacSHA224 extends HmacBase { + public HmacSHA224() { + super(KeyStoreKeyConstraints.Digest.SHA224); + } + } + + public static class HmacSHA256 extends HmacBase { + public HmacSHA256() { + super(KeyStoreKeyConstraints.Digest.SHA256); + } + } + + public static class HmacSHA384 extends HmacBase { + public HmacSHA384() { + super(KeyStoreKeyConstraints.Digest.SHA384); + } + } + + public static class HmacSHA512 extends HmacBase { + public HmacSHA512() { + super(KeyStoreKeyConstraints.Digest.SHA512); } } @@ -109,39 +138,40 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits; args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits); - @KeyStoreKeyConstraints.PurposeEnum int purposes = (spec.getPurposes() != null) - ? spec.getPurposes() - : (KeyStoreKeyConstraints.Purpose.ENCRYPT - | KeyStoreKeyConstraints.Purpose.DECRYPT - | KeyStoreKeyConstraints.Purpose.SIGN - | KeyStoreKeyConstraints.Purpose.VERIFY); + @KeyStoreKeyConstraints.PurposeEnum int purposes = spec.getPurposes(); + @KeyStoreKeyConstraints.BlockModeEnum int blockModes = spec.getBlockModes(); + if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) + && (spec.isRandomizedEncryptionRequired())) { + @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes = + blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES; + if (incompatibleBlockModes != 0) { + throw new IllegalStateException( + "Randomized encryption (IND-CPA) required but may be violated by block" + + " mode(s): " + + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes) + + ". See KeyGeneratorSpec documentation."); + } + } + for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); } - if (spec.getBlockMode() != null) { - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, - KeyStoreKeyConstraints.BlockMode.toKeymaster(spec.getBlockMode())); - } - if (spec.getPadding() != null) { - args.addInt(KeymasterDefs.KM_TAG_PADDING, - KeyStoreKeyConstraints.Padding.toKeymaster(spec.getPadding())); - } - if (spec.getMaxUsesPerBoot() != null) { - args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, spec.getMaxUsesPerBoot()); + for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) { + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode); } - if (spec.getMinSecondsBetweenOperations() != null) { - args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS, - spec.getMinSecondsBetweenOperations()); + for (int keymasterPadding : + KeyStoreKeyConstraints.Padding.allToKeymaster(spec.getPaddings())) { + args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding); } - if (spec.getUserAuthenticators().isEmpty()) { + if (spec.getUserAuthenticators() == 0) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); } else { args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster( spec.getUserAuthenticators())); } - if (spec.getUserAuthenticationValidityDurationSeconds() != null) { + if (spec.getUserAuthenticationValidityDurationSeconds() != -1) { args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, spec.getUserAuthenticationValidityDurationSeconds()); } @@ -156,8 +186,8 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE)); if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) - || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) { - // Permit caller-specified IV. This is needed due to the Cipher abstraction. + && (!spec.isRandomizedEncryptionRequired())) { + // Permit caller-provided IV when encrypting with this key args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); } diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java index ddeefbd..df4c958 100644 --- a/keystore/java/android/security/KeyStoreKeySpec.java +++ b/keystore/java/android/security/KeyStoreKeySpec.java @@ -17,10 +17,7 @@ package android.security; import java.security.spec.KeySpec; -import java.util.Collections; import java.util.Date; -import java.util.HashSet; -import java.util.Set; /** * Information about a key from the <a href="{@docRoot}training/articles/keystore.html">Android @@ -37,14 +34,12 @@ public class KeyStoreKeySpec implements KeySpec { private final Date mKeyValidityForConsumptionEnd; private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes; private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm; - private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; - private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest; - private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; - private final Integer mMinSecondsBetweenOperations; - private final Integer mMaxUsesPerBoot; - private final Set<Integer> mUserAuthenticators; - private final Set<Integer> mTeeBackedUserAuthenticators; - private final Integer mUserAuthenticationValidityDurationSeconds; + private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; + private final @KeyStoreKeyConstraints.DigestEnum int mDigests; + private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mTeeEnforcedUserAuthenticators; + private final int mUserAuthenticationValidityDurationSeconds; /** @@ -52,18 +47,18 @@ public class KeyStoreKeySpec implements KeySpec { */ KeyStoreKeySpec(String keystoreKeyAlias, @KeyStoreKeyCharacteristics.OriginEnum int origin, - int keySize, Date keyValidityStart, Date keyValidityForOriginationEnd, + int keySize, + Date keyValidityStart, + Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, @KeyStoreKeyConstraints.PurposeEnum int purposes, @KeyStoreKeyConstraints.AlgorithmEnum int algorithm, - @KeyStoreKeyConstraints.PaddingEnum Integer padding, - @KeyStoreKeyConstraints.DigestEnum Integer digest, - @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode, - Integer minSecondsBetweenOperations, - Integer maxUsesPerBoot, - Set<Integer> userAuthenticators, - Set<Integer> teeBackedUserAuthenticators, - Integer userAuthenticationValidityDurationSeconds) { + @KeyStoreKeyConstraints.PaddingEnum int paddings, + @KeyStoreKeyConstraints.DigestEnum int digests, + @KeyStoreKeyConstraints.BlockModeEnum int blockModes, + @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, + @KeyStoreKeyConstraints.UserAuthenticatorEnum int teeEnforcedUserAuthenticators, + int userAuthenticationValidityDurationSeconds) { mKeystoreAlias = keystoreKeyAlias; mOrigin = origin; mKeySize = keySize; @@ -72,17 +67,11 @@ public class KeyStoreKeySpec implements KeySpec { mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; mPurposes = purposes; mAlgorithm = algorithm; - mPadding = padding; - mDigest = digest; - mBlockMode = blockMode; - mMinSecondsBetweenOperations = minSecondsBetweenOperations; - mMaxUsesPerBoot = maxUsesPerBoot; - mUserAuthenticators = (userAuthenticators != null) - ? new HashSet<Integer>(userAuthenticators) - : Collections.<Integer>emptySet(); - mTeeBackedUserAuthenticators = (teeBackedUserAuthenticators != null) - ? new HashSet<Integer>(teeBackedUserAuthenticators) - : Collections.<Integer>emptySet(); + mPaddings = paddings; + mDigests = digests; + mBlockModes = blockModes; + mUserAuthenticators = userAuthenticators; + mTeeEnforcedUserAuthenticators = teeEnforcedUserAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -101,7 +90,7 @@ public class KeyStoreKeySpec implements KeySpec { } /** - * Gets the key's size in bits. + * Gets the size of the key in bits. */ public int getKeySize() { return mKeySize; @@ -149,78 +138,53 @@ public class KeyStoreKeySpec implements KeySpec { } /** - * Gets the only block mode with which the key can be used. - * - * @return block mode or {@code null} if the block mode is not restricted. + * Gets the set of block modes with which the key can be used. */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; + public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { + return mBlockModes; } /** - * Gets the only padding mode with which the key can be used. - * - * @return padding mode or {@code null} if the padding mode is not restricted. - */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; - } - - /** - * Gets the only digest algorithm with which the key can be used. - * - * @return digest algorithm or {@code null} if the digest algorithm is not restricted. + * Gets the set of padding modes with which the key can be used. */ - public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() { - return mDigest; + public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { + return mPaddings; } /** - * Gets the minimum number of seconds that must expire since the most recent use of the key - * before it can be used again. - * - * @return number of seconds or {@code null} if there is no restriction on how frequently a key - * can be used. - */ - public Integer getMinSecondsBetweenOperations() { - return mMinSecondsBetweenOperations; - } - - /** - * Gets the number of times the key can be used without rebooting the device. - * - * @return maximum number of times or {@code null} if there is no restriction. + * Gets the set of digest algorithms with which the key can be used. */ - public Integer getMaxUsesPerBoot() { - return mMaxUsesPerBoot; + public @KeyStoreKeyConstraints.DigestEnum int getDigests() { + return mDigests; } /** - * Gets the user authenticators which protect access to the key. The key can only be used iff - * the user has authenticated to at least one of these user authenticators. + * Gets the set of user authenticators which protect access to the key. The key can only be used + * iff the user has authenticated to at least one of these user authenticators. * - * @return user authenticators or empty set if the key can be used without user authentication. + * @return user authenticators or {@code 0} if the key can be used without user authentication. */ - public Set<Integer> getUserAuthenticators() { - return new HashSet<Integer>(mUserAuthenticators); + public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + return mUserAuthenticators; } /** - * Gets the TEE-backed user authenticators which protect access to the key. This is a subset of - * the user authentications returned by {@link #getUserAuthenticators()}. + * Gets the set of user authenticators for which the TEE enforces access restrictions for this + * key. This is a subset of the user authentications returned by + * {@link #getUserAuthenticators()}. */ - public Set<Integer> getTeeBackedUserAuthenticators() { - return new HashSet<Integer>(mTeeBackedUserAuthenticators); + public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getTeeEnforcedUserAuthenticators() { + return mTeeEnforcedUserAuthenticators; } /** * Gets the duration of time (seconds) for which the key can be used after the user * successfully authenticates to one of the associated user authenticators. * - * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication + * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication * is required for every use of the key. */ - public Integer getUserAuthenticationValidityDurationSeconds() { + public int getUserAuthenticationValidityDurationSeconds() { return mUserAuthenticationValidityDurationSeconds; } } diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java index 998e1d9..c9b7c36 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -18,12 +18,12 @@ package android.security; import android.content.Context; +import java.security.Key; import java.security.KeyPairGenerator; import java.security.KeyStore.ProtectionParameter; -import java.util.Collections; import java.util.Date; -import java.util.HashSet; -import java.util.Set; + +import javax.crypto.Cipher; /** * This provides the optional parameters that can be specified for @@ -50,31 +50,27 @@ public final class KeyStoreParameter implements ProtectionParameter { private final Date mKeyValidityStart; private final Date mKeyValidityForOriginationEnd; private final Date mKeyValidityForConsumptionEnd; - private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; - private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm; - private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; - private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest; - private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; - private final Integer mMinSecondsBetweenOperations; - private final Integer mMaxUsesPerBoot; - private final Set<Integer> mUserAuthenticators; - private final Integer mUserAuthenticationValidityDurationSeconds; + private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes; + private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; + private final @KeyStoreKeyConstraints.DigestEnum Integer mDigests; + private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private final boolean mRandomizedEncryptionRequired; + private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private final int mUserAuthenticationValidityDurationSeconds; private KeyStoreParameter(int flags, Date keyValidityStart, Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, - @KeyStoreKeyConstraints.PurposeEnum Integer purposes, - @KeyStoreKeyConstraints.AlgorithmEnum Integer algorithm, - @KeyStoreKeyConstraints.PaddingEnum Integer padding, - @KeyStoreKeyConstraints.DigestEnum Integer digest, - @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode, - Integer minSecondsBetweenOperations, - Integer maxUsesPerBoot, - Set<Integer> userAuthenticators, - Integer userAuthenticationValidityDurationSeconds) { - if ((userAuthenticationValidityDurationSeconds != null) - && (userAuthenticationValidityDurationSeconds < 0)) { + @KeyStoreKeyConstraints.PurposeEnum int purposes, + @KeyStoreKeyConstraints.PaddingEnum int paddings, + @KeyStoreKeyConstraints.DigestEnum Integer digests, + @KeyStoreKeyConstraints.BlockModeEnum int blockModes, + boolean randomizedEncryptionRequired, + @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, + int userAuthenticationValidityDurationSeconds) { + if ((userAuthenticationValidityDurationSeconds < 0) + && (userAuthenticationValidityDurationSeconds != -1)) { throw new IllegalArgumentException( "userAuthenticationValidityDurationSeconds must not be negative"); } @@ -84,15 +80,11 @@ public final class KeyStoreParameter implements ProtectionParameter { mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; mPurposes = purposes; - mAlgorithm = algorithm; - mPadding = padding; - mDigest = digest; - mBlockMode = blockMode; - mMinSecondsBetweenOperations = minSecondsBetweenOperations; - mMaxUsesPerBoot = maxUsesPerBoot; - mUserAuthenticators = (userAuthenticators != null) - ? new HashSet<Integer>(userAuthenticators) - : Collections.<Integer>emptySet(); + mPaddings = paddings; + mDigests = digests; + mBlockModes = blockModes; + mRandomizedEncryptionRequired = randomizedEncryptionRequired; + mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -144,105 +136,96 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * Gets the set of purposes for which the key can be used to the provided set of purposes. - * - * @return set of purposes or {@code null} if the key can be used for any purpose. + * Gets the set of purposes for which the key can be used. * * @hide */ - public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() { + public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() { return mPurposes; } /** - * Gets the algorithm to which the key is restricted. + * Gets the set of padding schemes to which the key is restricted. * - * @return algorithm or {@code null} if it's not restricted. * @hide */ - public @KeyStoreKeyConstraints.AlgorithmEnum Integer getAlgorithm() { - return mAlgorithm; + public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { + return mPaddings; } /** - * Gets the padding scheme to which the key is restricted. + * Gets the set of digests to which the key is restricted. * - * @return padding scheme or {@code null} if the padding scheme is not restricted. - * - * @hide - */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; - } - - /** - * Gets the digest to which the key is restricted when generating signatures or Message - * Authentication Codes (MACs). + * @throws IllegalStateException if this restriction has not been specified. * - * @return digest or {@code null} if the digest is not restricted. + * @see #isDigestsSpecified() * * @hide */ - public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() { - return mDigest; + public @KeyStoreKeyConstraints.DigestEnum int getDigests() { + if (mDigests == null) { + throw new IllegalStateException("Digests not specified"); + } + return mDigests; } /** - * Gets the block mode to which the key is restricted when used for encryption or decryption. + * Returns {@code true} if digest restrictions have been specified. * - * @return block more or {@code null} if block mode is not restricted. + * @see #getDigests() * * @hide */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; + public boolean isDigestsSpecified() { + return mDigests != null; } /** - * Gets the minimum number of seconds that must expire since the most recent use of the key - * before it can be used again. - * - * @return number of seconds or {@code null} if there is no restriction on how frequently a key - * can be used. + * Gets the set of block modes to which the key is restricted. * * @hide */ - public Integer getMinSecondsBetweenOperations() { - return mMinSecondsBetweenOperations; + public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { + return mBlockModes; } /** - * Gets the number of times the key can be used without rebooting the device. + * Returns {@code true} if encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic property + * being required is <em>indistinguishability under chosen-plaintext attack ({@code + * IND-CPA})</em>. This property is important because it mitigates several classes of + * weaknesses due to which ciphertext may leak information about plaintext. For example, if a + * given plaintext always produces the same ciphertext, an attacker may see the repeated + * ciphertexts and be able to deduce something about the plaintext. * - * @return maximum number of times or {@code null} if there is no restriction. * @hide */ - public Integer getMaxUsesPerBoot() { - return mMaxUsesPerBoot; + public boolean isRandomizedEncryptionRequired() { + return mRandomizedEncryptionRequired; } /** - * Gets the user authenticators which protect access to this key. The key can only be used iff - * the user has authenticated to at least one of these user authenticators. + * Gets the set of user authenticators which protect access to this key. The key can only be + * used iff the user has authenticated to at least one of these user authenticators. * - * @return user authenticators or empty set if the key can be used without user authentication. + * @return user authenticators or {@code 0} if the key can be used without user authentication. * * @hide */ - public Set<Integer> getUserAuthenticators() { - return new HashSet<Integer>(mUserAuthenticators); + public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + return mUserAuthenticators; } /** * Gets the duration of time (seconds) for which this key can be used after the user * successfully authenticates to one of the associated user authenticators. * - * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication + * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication * is required for every use of the key. * * @hide */ - public Integer getUserAuthenticationValidityDurationSeconds() { + public int getUserAuthenticationValidityDurationSeconds() { return mUserAuthenticationValidityDurationSeconds; } @@ -268,15 +251,13 @@ public final class KeyStoreParameter implements ProtectionParameter { private Date mKeyValidityStart; private Date mKeyValidityForOriginationEnd; private Date mKeyValidityForConsumptionEnd; - private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; - private @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm; - private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; - private @KeyStoreKeyConstraints.DigestEnum Integer mDigest; - private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; - private Integer mMinSecondsBetweenOperations; - private Integer mMaxUsesPerBoot; - private Set<Integer> mUserAuthenticators; - private Integer mUserAuthenticationValidityDurationSeconds; + private @KeyStoreKeyConstraints.PurposeEnum int mPurposes; + private @KeyStoreKeyConstraints.PaddingEnum int mPaddings; + private @KeyStoreKeyConstraints.DigestEnum Integer mDigests; + private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private boolean mRandomizedEncryptionRequired = true; + private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private int mUserAuthenticationValidityDurationSeconds = -1; /** * Creates a new instance of the {@code Builder} with the given @@ -368,9 +349,9 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * Restricts the purposes for which the key can be used to the provided set of purposes. + * Restricts the key to being used only for the provided set of purposes. * - * <p>By default, the key can be used for encryption, decryption, signing, and verification. + * <p>This restriction must be specified. There is no default. * * @hide */ @@ -380,84 +361,84 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * Sets the algorithm of the key. - * - * <p>The algorithm of symmetric keys can be deduced from the key itself. Thus, explicitly - * specifying the algorithm of symmetric keys using this method is not necessary. - * - * @hide - */ - public Builder setAlgorithm(@KeyStoreKeyConstraints.AlgorithmEnum int algorithm) { - mAlgorithm = algorithm; - return this; - } - - /** - * Restricts the key to being used only with the provided padding scheme. Attempts to use + * Restricts the key to being used only with the provided padding schemes. Attempts to use * the key with any other padding will be rejected. * * <p>This restriction must be specified for keys which are used for encryption/decryption. * * @hide */ - public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) { - mPadding = padding; - return this; - } - - /** - * Restricts the key to being used only with the provided digest when generating signatures - * or Message Authentication Codes (MACs). Attempts to use the key with any other digest - * will be rejected. - * - * <p>For MAC keys, the default is to restrict to the digest specified in the key algorithm - * name. For asymmetric signing keys this constraint must be specified because there is no - * default. - * - * @see java.security.Key#getAlgorithm() - * - * @hide - */ - public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) { - mDigest = digest; + public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) { + mPaddings = paddings; return this; } /** - * Restricts the key to being used only with the provided block mode when encrypting or - * decrypting. Attempts to use the key with any other block modes will be rejected. + * Restricts the key to being used only with the provided digests when generating signatures + * or HMACs. Attempts to use the key with any other digest will be rejected. * - * <p>This restriction must be specified for keys which are used for encryption/decryption. + * <p>For HMAC keys, the default is to restrict to the digest specified in + * {@link Key#getAlgorithm()}. For asymmetric signing keys this constraint must be specified + * because there is no default. * * @hide */ - public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) { - mBlockMode = blockMode; + public Builder setDigests(@KeyStoreKeyConstraints.DigestEnum int digests) { + mDigests = digests; return this; } /** - * Sets the minimum number of seconds that must expire since the most recent use of the key - * before it can be used again. + * Restricts the key to being used only with the provided block modes. Attempts to use the + * key with any other block modes will be rejected. * - * <p>By default, there is no restriction on how frequently a key can be used. + * <p>This restriction must be specified for symmetric encryption/decryption keys. * * @hide */ - public Builder setMinSecondsBetweenOperations(int seconds) { - mMinSecondsBetweenOperations = seconds; + public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) { + mBlockModes = blockModes; return this; } /** - * Sets the maximum number of times a key can be used without rebooting the device. - * - * <p>By default, the key can be used for an unlimited number of times. + * Sets whether encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic + * property being required is <em>indistinguishability under chosen-plaintext attack + * ({@code IND-CPA})</em>. This property is important because it mitigates several classes + * of weaknesses due to which ciphertext may leak information about plaintext. For example, + * if a given plaintext always produces the same ciphertext, an attacker may see the + * repeated ciphertexts and be able to deduce something about the plaintext. + * + * <p>By default, {@code IND-CPA} is required. + * + * <p>When {@code IND-CPA} is required: + * <ul> + * <li>transformation which do not offer {@code IND-CPA}, such as symmetric ciphers using + * {@code ECB} mode or RSA encryption without padding, are prohibited;</li> + * <li>in transformations which use an IV, such as symmetric ciphers in {@code CBC}, + * {@code CTR}, and {@code GCM} block modes, caller-provided IVs are rejected when + * encrypting, to ensure that only random IVs are used.</li> + * + * <p>Before disabling this requirement, consider the following approaches instead: + * <ul> + * <li>If you are generating a random IV for encryption and then initializing a {@code} + * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV + * instead. This will occur if the {@code Cipher} is initialized for encryption without an + * IV. The IV can then be queried via {@link Cipher#getIV()}.</li> + * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully + * random, such as the name of the file being encrypted, or transaction ID, or password, + * or a device identifier), consider changing your design to use a random IV which will then + * be provided in addition to the ciphertext to the entities which need to decrypt the + * ciphertext.</li> + * <li>If you are using RSA encryption without padding, consider switching to padding + * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li> + * </ul> * * @hide */ - public Builder setMaxUsesPerBoot(int count) { - mMaxUsesPerBoot = count; + public Builder setRandomizedEncryptionRequired(boolean required) { + mRandomizedEncryptionRequired = required; return this; } @@ -467,16 +448,16 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p>By default, the key can be used without user authentication. * - * @param userAuthenticators user authenticators or empty list if this key can be accessed + * @param userAuthenticators user authenticators or {@code 0} if this key can be accessed * without user authentication. * * @see #setUserAuthenticationValidityDurationSeconds(int) * * @hide */ - public Builder setUserAuthenticators(Set<Integer> userAuthenticators) { - mUserAuthenticators = - (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null; + public Builder setUserAuthenticators( + @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) { + mUserAuthenticators = userAuthenticators; return this; } @@ -489,7 +470,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for * every use of the key. * - * @see #setUserAuthenticators(Set) + * @see #setUserAuthenticators(int) * * @hide */ @@ -510,12 +491,10 @@ public final class KeyStoreParameter implements ProtectionParameter { mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, - mAlgorithm, - mPadding, - mDigest, - mBlockMode, - mMinSecondsBetweenOperations, - mMaxUsesPerBoot, + mPaddings, + mDigests, + mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); } diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java index f552759..09f0b00 100644 --- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java @@ -23,7 +23,6 @@ import java.security.InvalidKeyException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.util.Date; -import java.util.Set; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactorySpi; @@ -75,9 +74,11 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { int keySize; @KeyStoreKeyConstraints.PurposeEnum int purposes; @KeyStoreKeyConstraints.AlgorithmEnum int algorithm; - @KeyStoreKeyConstraints.PaddingEnum Integer padding; - @KeyStoreKeyConstraints.DigestEnum Integer digest; - @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode; + @KeyStoreKeyConstraints.PaddingEnum int paddings; + @KeyStoreKeyConstraints.DigestEnum int digests; + @KeyStoreKeyConstraints.BlockModeEnum int blockModes; + @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators; + @KeyStoreKeyConstraints.UserAuthenticatorEnum int teeEnforcedUserAuthenticators; try { origin = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ORIGIN); if (origin == null) { @@ -97,18 +98,27 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { throw new InvalidKeySpecException("Key algorithm not available"); } algorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(alg); - padding = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING); - if (padding != null) { - padding = KeyStoreKeyConstraints.Padding.fromKeymaster(padding); - } - digest = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST); - if (digest != null) { - digest = KeyStoreKeyConstraints.Digest.fromKeymaster(digest); - } - blockMode = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE); - if (blockMode != null) { - blockMode = KeyStoreKeyConstraints.BlockMode.fromKeymaster(blockMode); - } + paddings = KeyStoreKeyConstraints.Padding.allFromKeymaster( + KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING)); + digests = KeyStoreKeyConstraints.Digest.allFromKeymaster( + KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST)); + blockModes = KeyStoreKeyConstraints.BlockMode.allFromKeymaster( + KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE)); + + @KeyStoreKeyConstraints.UserAuthenticatorEnum + int swEnforcedKeymasterUserAuthenticators = + keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); + @KeyStoreKeyConstraints.UserAuthenticatorEnum + int hwEnforcedKeymasterUserAuthenticators = + keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); + @KeyStoreKeyConstraints.UserAuthenticatorEnum + int keymasterUserAuthenticators = + swEnforcedKeymasterUserAuthenticators | hwEnforcedKeymasterUserAuthenticators; + userAuthenticators = KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster( + keymasterUserAuthenticators); + teeEnforcedUserAuthenticators = + KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster( + hwEnforcedKeymasterUserAuthenticators); } catch (IllegalArgumentException e) { throw new InvalidKeySpecException("Unsupported key characteristic", e); } @@ -130,17 +140,8 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { && (keyValidityForConsumptionEnd.getTime() == Long.MAX_VALUE)) { keyValidityForConsumptionEnd = null; } - - int swEnforcedUserAuthenticatorIds = - keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); - int hwEnforcedUserAuthenticatorIds = - keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); - int userAuthenticatorIds = swEnforcedUserAuthenticatorIds | hwEnforcedUserAuthenticatorIds; - Set<Integer> userAuthenticators = - KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(userAuthenticatorIds); - Set<Integer> teeBackedUserAuthenticators = - KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster( - hwEnforcedUserAuthenticatorIds); + Integer userAuthenticationValidityDurationSeconds = + KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT); return new KeyStoreKeySpec(entryAlias, origin, @@ -150,15 +151,13 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { keyValidityForConsumptionEnd, purposes, algorithm, - padding, - digest, - blockMode, - KeymasterUtils.getInt(keyCharacteristics, - KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS), - KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT), + paddings, + digests, + blockModes, userAuthenticators, - teeBackedUserAuthenticators, - KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT)); + teeEnforcedUserAuthenticators, + ((userAuthenticationValidityDurationSeconds != null) + ? userAuthenticationValidityDurationSeconds : -1)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index e66934e..fae0643 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -773,6 +773,11 @@ public class KeyguardViewMediator extends SystemUI { synchronized (this) { if (DEBUG) Log.d(TAG, "setKeyguardEnabled(" + enabled + ")"); + if (isSecure()) { + Log.d(TAG, "current mode is SecurityMode, ignore hide keyguard"); + return; + } + mExternallyEnabled = enabled; if (!enabled && mShowing) { diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java index 0f967fc..de1c497 100644 --- a/rs/java/android/renderscript/FieldPacker.java +++ b/rs/java/android/renderscript/FieldPacker.java @@ -47,6 +47,15 @@ public class FieldPacker { // subAlign() can never work correctly for copied FieldPacker objects. } + static FieldPacker createFromArray(Object[] args) { + FieldPacker fp = new FieldPacker(RenderScript.sPointerSize * 8); + for (Object arg : args) { + fp.addSafely(arg); + } + fp.resize(fp.mPos); + return fp; + } + public void align(int v) { if ((v <= 0) || ((v & (v - 1)) != 0)) { throw new RSIllegalArgumentException("argument must be a non-negative non-zero power of 2: " + v); @@ -618,294 +627,182 @@ public class FieldPacker { return mPos; } - private static void addToPack(FieldPacker fp, Object obj) { + private void add(Object obj) { if (obj instanceof Boolean) { - fp.addBoolean(((Boolean)obj).booleanValue()); + addBoolean((Boolean)obj); return; } if (obj instanceof Byte) { - fp.addI8(((Byte)obj).byteValue()); + addI8((Byte)obj); return; } if (obj instanceof Short) { - fp.addI16(((Short)obj).shortValue()); + addI16((Short)obj); return; } if (obj instanceof Integer) { - fp.addI32(((Integer)obj).intValue()); + addI32((Integer)obj); return; } if (obj instanceof Long) { - fp.addI64(((Long)obj).longValue()); + addI64((Long)obj); return; } if (obj instanceof Float) { - fp.addF32(((Float)obj).floatValue()); + addF32((Float)obj); return; } if (obj instanceof Double) { - fp.addF64(((Double)obj).doubleValue()); + addF64((Double)obj); return; } if (obj instanceof Byte2) { - fp.addI8((Byte2)obj); + addI8((Byte2)obj); return; } if (obj instanceof Byte3) { - fp.addI8((Byte3)obj); + addI8((Byte3)obj); return; } if (obj instanceof Byte4) { - fp.addI8((Byte4)obj); + addI8((Byte4)obj); return; } if (obj instanceof Short2) { - fp.addI16((Short2)obj); + addI16((Short2)obj); return; } if (obj instanceof Short3) { - fp.addI16((Short3)obj); + addI16((Short3)obj); return; } if (obj instanceof Short4) { - fp.addI16((Short4)obj); + addI16((Short4)obj); return; } if (obj instanceof Int2) { - fp.addI32((Int2)obj); + addI32((Int2)obj); return; } if (obj instanceof Int3) { - fp.addI32((Int3)obj); + addI32((Int3)obj); return; } if (obj instanceof Int4) { - fp.addI32((Int4)obj); + addI32((Int4)obj); return; } if (obj instanceof Long2) { - fp.addI64((Long2)obj); + addI64((Long2)obj); return; } if (obj instanceof Long3) { - fp.addI64((Long3)obj); + addI64((Long3)obj); return; } if (obj instanceof Long4) { - fp.addI64((Long4)obj); + addI64((Long4)obj); return; } if (obj instanceof Float2) { - fp.addF32((Float2)obj); + addF32((Float2)obj); return; } if (obj instanceof Float3) { - fp.addF32((Float3)obj); + addF32((Float3)obj); return; } if (obj instanceof Float4) { - fp.addF32((Float4)obj); + addF32((Float4)obj); return; } if (obj instanceof Double2) { - fp.addF64((Double2)obj); + addF64((Double2)obj); return; } if (obj instanceof Double3) { - fp.addF64((Double3)obj); + addF64((Double3)obj); return; } if (obj instanceof Double4) { - fp.addF64((Double4)obj); + addF64((Double4)obj); return; } if (obj instanceof Matrix2f) { - fp.addMatrix((Matrix2f)obj); + addMatrix((Matrix2f)obj); return; } if (obj instanceof Matrix3f) { - fp.addMatrix((Matrix3f)obj); + addMatrix((Matrix3f)obj); return; } if (obj instanceof Matrix4f) { - fp.addMatrix((Matrix4f)obj); + addMatrix((Matrix4f)obj); return; } if (obj instanceof BaseObj) { - fp.addObj((BaseObj)obj); + addObj((BaseObj)obj); return; } } - private static int getPackedSize(Object obj) { - if (obj instanceof Boolean) { - return 1; - } - - if (obj instanceof Byte) { - return 1; - } - - if (obj instanceof Short) { - return 2; - } - - if (obj instanceof Integer) { - return 4; - } - - if (obj instanceof Long) { - return 8; - } - - if (obj instanceof Float) { - return 4; - } - - if (obj instanceof Double) { - return 8; - } - - if (obj instanceof Byte2) { - return 2; - } - - if (obj instanceof Byte3) { - return 3; - } - - if (obj instanceof Byte4) { - return 4; - } - - if (obj instanceof Short2) { - return 4; - } - - if (obj instanceof Short3) { - return 6; - } - - if (obj instanceof Short4) { - return 8; - } - - if (obj instanceof Int2) { - return 8; - } - - if (obj instanceof Int3) { - return 12; - } - - if (obj instanceof Int4) { - return 16; - } - - if (obj instanceof Long2) { - return 16; - } - - if (obj instanceof Long3) { - return 24; - } - - if (obj instanceof Long4) { - return 32; - } - - if (obj instanceof Float2) { - return 8; - } - - if (obj instanceof Float3) { - return 12; + private boolean resize(int newSize) { + if (newSize == mLen) { + return false; } - if (obj instanceof Float4) { - return 16; - } - - if (obj instanceof Double2) { - return 16; - } - - if (obj instanceof Double3) { - return 24; - } - - if (obj instanceof Double4) { - return 32; - } - - if (obj instanceof Matrix2f) { - return 16; - } - - if (obj instanceof Matrix3f) { - return 36; - } - - if (obj instanceof Matrix4f) { - return 64; - } - - if (obj instanceof BaseObj) { - if (RenderScript.sPointerSize == 8) { - return 32; - } else { - return 4; - } - } - - return 0; + byte[] newData = new byte[newSize]; + System.arraycopy(mData, 0, newData, 0, mPos); + mData = newData; + mLen = newSize; + return true; } - static FieldPacker createFieldPack(Object[] args) { - int len = 0; - for (Object arg : args) { - len += getPackedSize(arg); - } - FieldPacker fp = new FieldPacker(len); - for (Object arg : args) { - addToPack(fp, arg); - } - return fp; + private void addSafely(Object obj) { + boolean retry; + final int oldPos = mPos; + do { + retry = false; + try { + add(obj); + } catch (ArrayIndexOutOfBoundsException e) { + mPos = oldPos; + resize(mLen * 2); + retry = true; + } + } while (retry); } - private final byte mData[]; + private byte mData[]; private int mPos; private int mLen; private BitSet mAlignment; - } - - diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index 45f0ca6..5138719 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -16,7 +16,6 @@ package android.renderscript; -import java.io.File; import java.lang.reflect.Method; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -130,8 +129,6 @@ public class RenderScript { native void nContextInitToClient(long con); native void nContextDeinitToClient(long con); - static File mCacheDir; - // this should be a monotonically increasing ID // used in conjunction with the API version of a device static final long sMinorID = 1; @@ -146,23 +143,6 @@ public class RenderScript { return sMinorID; } - /** - * Sets the directory to use as a persistent storage for the - * renderscript object file cache. - * - * @hide - * @param cacheDir A directory the current process can write to - */ - public static void setupDiskCache(File cacheDir) { - if (!sInitialized) { - Log.e(LOG_TAG, "RenderScript.setupDiskCache() called when disabled"); - return; - } - - // Defer creation of cache path to nScriptCCreate(). - mCacheDir = cacheDir; - } - /** * ContextType specifies the specific type of context to be created. * diff --git a/rs/java/android/renderscript/RenderScriptCacheDir.java b/rs/java/android/renderscript/RenderScriptCacheDir.java new file mode 100644 index 0000000..95a9d75 --- /dev/null +++ b/rs/java/android/renderscript/RenderScriptCacheDir.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008-2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.renderscript; + +import java.io.File; + +/** + * Used only for tracking the RenderScript cache directory. + * @hide + */ +public class RenderScriptCacheDir { + /** + * Sets the directory to use as a persistent storage for the + * renderscript object file cache. + * + * @hide + * @param cacheDir A directory the current process can write to + */ + public static void setupDiskCache(File cacheDir) { + // Defer creation of cache path to nScriptCCreate(). + mCacheDir = cacheDir; + } + + static File mCacheDir; + +} diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java index 64d21e4..bf706c1 100644 --- a/rs/java/android/renderscript/ScriptC.java +++ b/rs/java/android/renderscript/ScriptC.java @@ -124,7 +124,7 @@ public class ScriptC extends Script { // Create the RS cache path if we haven't done so already. if (mCachePath == null) { - File f = new File(rs.mCacheDir, CACHE_PATH); + File f = new File(RenderScriptCacheDir.mCacheDir, CACHE_PATH); mCachePath = f.getAbsolutePath(); f.mkdirs(); } @@ -135,7 +135,7 @@ public class ScriptC extends Script { private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) { // Create the RS cache path if we haven't done so already. if (mCachePath == null) { - File f = new File(rs.mCacheDir, CACHE_PATH); + File f = new File(RenderScriptCacheDir.mCacheDir, CACHE_PATH); mCachePath = f.getAbsolutePath(); f.mkdirs(); } diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java index 13e22aa..857e9fb 100644 --- a/rs/java/android/renderscript/ScriptGroup2.java +++ b/rs/java/android/renderscript/ScriptGroup2.java @@ -112,7 +112,7 @@ public class ScriptGroup2 extends BaseObj { public Closure(RenderScript rs, Script.InvokeID invokeID, Object[] args, Map<Script.FieldID, Object> globals) { super(0, rs); - mFP = FieldPacker.createFieldPack(args); + mFP = FieldPacker.createFromArray(args); mArgs = args; mBindings = globals; diff --git a/rs/java/android/renderscript/ScriptIntrinsicBlur.java b/rs/java/android/renderscript/ScriptIntrinsicBlur.java index 5c4edd3..60e2b6d 100644 --- a/rs/java/android/renderscript/ScriptIntrinsicBlur.java +++ b/rs/java/android/renderscript/ScriptIntrinsicBlur.java @@ -34,7 +34,7 @@ public final class ScriptIntrinsicBlur extends ScriptIntrinsic { * Create an intrinsic for applying a blur to an allocation. The * default radius is 5.0. * - * Supported elements types are {@link Element#U8_4} + * Supported elements types are {@link Element#U8_4 Element#U8} * * @param rs The RenderScript context * @param e Element type for inputs and outputs diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d9ef766..758b0fc 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2022,6 +2022,8 @@ public class ConnectivityService extends IConnectivityManager.Stub ReapUnvalidatedNetworks.DONT_REAP); } } + NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(msg.replyTo); + if (DBG && nfi != null) log("unregisterNetworkFactory for " + nfi.name); } // If this method proves to be too slow then we can maintain a separate diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index a9d6a69..4e7aa77 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2228,7 +2228,7 @@ public final class ActiveServices { EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH, sr.userId, sr.crashCount, sr.shortName, app.pid); bringDownServiceLocked(sr); - } else if (!allowRestart) { + } else if (!allowRestart || !mAm.isUserRunningLocked(sr.userId, false)) { bringDownServiceLocked(sr); } else { boolean canceled = scheduleServiceRestartLocked(sr, true); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 82c71e3..ada16e7 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -899,6 +899,11 @@ final class ActivityStack { r.userId, System.identityHashCode(r), r.shortComponentName, mPausingActivity != null ? mPausingActivity.shortComponentName : "(none)"); + if (r.finishing && r.state == ActivityState.PAUSING) { + if (DEBUG_PAUSE) Slog.v(TAG, + "Executing finish of failed to pause activity: " + r); + finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false); + } } } } @@ -3900,16 +3905,18 @@ final class ActivityStack { } void getTasksLocked(List<RunningTaskInfo> list, int callingUid, boolean allowed) { + boolean focusedStack = mStackSupervisor.getFocusedStack() == this; + boolean topTask = true; for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); + if (task.getTopActivity() == null) { + continue; + } ActivityRecord r = null; ActivityRecord top = null; int numActivities = 0; int numRunning = 0; final ArrayList<ActivityRecord> activities = task.mActivities; - if (activities.isEmpty()) { - continue; - } if (!allowed && !task.isHomeTask() && task.effectiveUid != callingUid) { continue; } @@ -3938,14 +3945,18 @@ final class ActivityStack { ci.baseActivity = r.intent.getComponent(); ci.topActivity = top.intent.getComponent(); ci.lastActiveTime = task.lastActiveTime; + if (focusedStack && topTask) { + // Give the latest time to ensure foreground task can be sorted + // at the first, because lastActiveTime of creating task is 0. + ci.lastActiveTime = System.currentTimeMillis(); + topTask = false; + } if (top.task != null) { ci.description = top.task.lastDescription; } ci.numActivities = numActivities; ci.numRunning = numRunning; - //System.out.println( - // "#" + maxNum + ": " + " descr=" + ci.description); list.add(ci); } } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index b4a44a6..3215acb 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -55,43 +55,25 @@ public final class Installer extends SystemService { return mInstaller.execute(builder.toString()); } - public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName, - String instructionSet) { + public int dexopt(String apkPath, int uid, boolean isPublic, + String instructionSet, int dexoptNeeded) { if (!isValidInstructionSet(instructionSet)) { Slog.e(TAG, "Invalid instruction set: " + instructionSet); return -1; } - return mInstaller.patchoat(apkPath, uid, isPublic, pkgName, instructionSet); - } - - public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) { - if (!isValidInstructionSet(instructionSet)) { - Slog.e(TAG, "Invalid instruction set: " + instructionSet); - return -1; - } - - return mInstaller.patchoat(apkPath, uid, isPublic, instructionSet); - } - - public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) { - if (!isValidInstructionSet(instructionSet)) { - Slog.e(TAG, "Invalid instruction set: " + instructionSet); - return -1; - } - - return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet); + return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet, dexoptNeeded); } public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName, - String instructionSet, boolean vmSafeMode, boolean debuggable, - @Nullable String outputPath) { + String instructionSet, int dexoptNeeded, boolean vmSafeMode, + boolean debuggable, @Nullable String outputPath) { if (!isValidInstructionSet(instructionSet)) { Slog.e(TAG, "Invalid instruction set: " + instructionSet); return -1; } - - return mInstaller.dexopt(apkPath, uid, isPublic, pkgName, instructionSet, vmSafeMode, + return mInstaller.dexopt(apkPath, uid, isPublic, pkgName, + instructionSet, dexoptNeeded, vmSafeMode, debuggable, outputPath); } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 680ec4b..4c36fa6 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -113,64 +113,48 @@ final class PackageDexOptimizer { for (String path : paths) { try { - // This will return DEXOPT_NEEDED if we either cannot find any odex file for this - // package or the one we find does not match the image checksum (i.e. it was - // compiled against an old image). It will return PATCHOAT_NEEDED if we can find a - // odex file and it matches the checksum of the image but not its base address, - // meaning we need to move it. - final byte isDexOptNeeded = DexFile.isDexOptNeededInternal(path, - pkg.packageName, dexCodeInstructionSet, defer); - if (forceDex || (!defer && isDexOptNeeded == DexFile.DEXOPT_NEEDED)) { - File oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet); - Log.i(TAG, "Running dexopt on: " + path + " pkg=" - + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet - + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable - + " oatDir = " + oatDir); - final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); + final int dexoptNeeded; + if (forceDex) { + dexoptNeeded = DexFile.DEX2OAT_NEEDED; + } else { + dexoptNeeded = DexFile.getDexOptNeeded(path, + pkg.packageName, dexCodeInstructionSet, defer); + } - if (oatDir != null) { - int ret = mPackageManagerService.mInstaller.dexopt( - path, sharedGid, !pkg.isForwardLocked(), pkg.packageName, - dexCodeInstructionSet, vmSafeMode, debuggable, - oatDir.getAbsolutePath()); - if (ret < 0) { - return DEX_OPT_FAILED; - } + if (!forceDex && defer && dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { + // We're deciding to defer a needed dexopt. Don't bother dexopting for other + // paths and instruction sets. We'll deal with them all together when we process + // our list of deferred dexopts. + addPackageForDeferredDexopt(pkg); + return DEX_OPT_DEFERRED; + } + + if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { + final String dexoptType; + String oatDir = null; + if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) { + dexoptType = "dex2oat"; + oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet); + } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) { + dexoptType = "patchoat"; + } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) { + dexoptType = "self patchoat"; } else { - final int ret = mPackageManagerService.mInstaller - .dexopt(path, sharedGid, - !pkg.isForwardLocked(), pkg.packageName, - dexCodeInstructionSet, - vmSafeMode, debuggable, null); - if (ret < 0) { - return DEX_OPT_FAILED; - } + throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded); } - - performedDexOpt = true; - } else if (!defer && isDexOptNeeded == DexFile.PATCHOAT_NEEDED) { - Log.i(TAG, "Running patchoat on: " + pkg.applicationInfo.packageName); + Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg=" + + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet + + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable + + " oatDir = " + oatDir); final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); - final int ret = mPackageManagerService.mInstaller.patchoat(path, sharedGid, - !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet); - + final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid, + !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet, + dexoptNeeded, vmSafeMode, debuggable, oatDir); if (ret < 0) { - // Don't bother running patchoat again if we failed, it will probably - // just result in an error again. Also, don't bother dexopting for other - // paths & ISAs. return DEX_OPT_FAILED; } - performedDexOpt = true; } - - // We're deciding to defer a needed dexopt. Don't bother dexopting for other - // paths and instruction sets. We'll deal with them all together when we process - // our list of deferred dexopts. - if (defer && isDexOptNeeded != DexFile.UP_TO_DATE) { - addPackageForDeferredDexopt(pkg); - return DEX_OPT_DEFERRED; - } } catch (FileNotFoundException e) { Slog.w(TAG, "Apk not found for dexopt: " + path); return DEX_OPT_FAILED; @@ -187,7 +171,7 @@ final class PackageDexOptimizer { } // At this point we haven't failed dexopt and we haven't deferred dexopt. We must - // either have either succeeded dexopt, or have had isDexOptNeededInternal tell us + // either have either succeeded dexopt, or have had getDexOptNeeded tell us // it isn't required. We therefore mark that this package doesn't need dexopt unless // it's forced. performedDexOpt will tell us whether we performed dex-opt or skipped // it. @@ -209,10 +193,11 @@ final class PackageDexOptimizer { * <li>Package location is not a directory, i.e. monolithic install.</li> * </ul> * - * @return oat directory or null, if oat directory cannot be created. + * @return Absolute path to the oat directory or null, if oat directory + * cannot be created. */ @Nullable - private File createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet) + private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet) throws IOException { if (pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) { return null; @@ -222,7 +207,7 @@ final class PackageDexOptimizer { File oatDir = getOatDir(codePath); mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(), dexInstructionSet); - return oatDir; + return oatDir.getAbsolutePath(); } return null; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 67d3cbe..3f0e8b0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1457,18 +1457,10 @@ public class PackageManagerService extends IPackageManager.Stub { } try { - byte dexoptRequired = DexFile.isDexOptNeededInternal(lib, null, - dexCodeInstructionSet, - false); - if (dexoptRequired != DexFile.UP_TO_DATE) { + int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false); + if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { alreadyDexOpted.add(lib); - - // The list of "shared libraries" we have at this point is - if (dexoptRequired == DexFile.DEXOPT_NEEDED) { - mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet); - } else { - mInstaller.patchoat(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet); - } + mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded); } } catch (FileNotFoundException e) { Slog.w(TAG, "Library not found: " + lib); @@ -1514,13 +1506,9 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } try { - byte dexoptRequired = DexFile.isDexOptNeededInternal(path, null, - dexCodeInstructionSet, - false); - if (dexoptRequired == DexFile.DEXOPT_NEEDED) { - mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet); - } else if (dexoptRequired == DexFile.PATCHOAT_NEEDED) { - mInstaller.patchoat(path, Process.SYSTEM_UID, true, dexCodeInstructionSet); + int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false); + if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { + mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded); } } catch (FileNotFoundException e) { Slog.w(TAG, "Jar not found: " + path); @@ -10447,13 +10435,13 @@ public class PackageManagerService extends IPackageManager.Stub { return; } + // Call with SCAN_NO_DEX, since dexopt has already been made if (replace) { - // Call replacePackageLI with SCAN_NO_DEX, since we already made dexopt replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING | SCAN_NO_DEX, args.user, installerPackageName, res); } else { - installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, - args.user, installerPackageName, res); + installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES + | SCAN_NO_DEX, args.user, installerPackageName, res); } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index de7cb33..8998d39 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -504,7 +504,12 @@ public class WindowManagerService extends IWindowManager.Stub int mLastDisplayFreezeDuration = 0; Object mLastFinishedFreezeSource = null; boolean mWaitingForConfig = false; - boolean mWindowsFreezingScreen = false; + + final static int WINDOWS_FREEZING_SCREENS_NONE = 0; + final static int WINDOWS_FREEZING_SCREENS_ACTIVE = 1; + final static int WINDOWS_FREEZING_SCREENS_TIMEOUT = 2; + private int mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE; + boolean mClientFreezingScreen = false; int mAppsFreezingScreen = 0; int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -4712,7 +4717,8 @@ public class WindowManagerService extends IWindowManager.Stub WindowState w = wtoken.allAppWindows.get(i); if (w.mAppFreezing) { w.mAppFreezing = false; - if (w.mHasSurface && !w.mOrientationChanging) { + if (w.mHasSurface && !w.mOrientationChanging + && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) { if (DEBUG_ORIENTATION) Slog.v(TAG, "set mOrientationChanging of " + w); w.mOrientationChanging = true; mInnerFields.mOrientationChangeComplete = false; @@ -4761,7 +4767,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mAppsFreezingScreen == 1) { startFreezingDisplayLocked(false, 0, 0); mH.removeMessages(H.APP_FREEZE_TIMEOUT); - mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 5000); + mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000); } } final int N = wtoken.allAppWindows.size(); @@ -6550,7 +6556,7 @@ public class WindowManagerService extends IWindowManager.Stub mAltOrientation = altOrientation; mPolicy.setRotationLw(mRotation); - mWindowsFreezingScreen = true; + mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION); mWaitingForConfig = true; @@ -7920,6 +7926,7 @@ public class WindowManagerService extends IWindowManager.Stub // TODO(multidisplay): Can non-default displays rotate? synchronized (mWindowMap) { Slog.w(TAG, "Window freeze timeout expired."); + mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT; final WindowList windows = getDefaultWindowListLocked(); int i = windows.size(); while (i > 0) { @@ -7991,6 +7998,7 @@ public class WindowManagerService extends IWindowManager.Stub case APP_FREEZE_TIMEOUT: { synchronized (mWindowMap) { Slog.w(TAG, "App freeze timeout expired."); + mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT; final int numStacks = mStackIdToStack.size(); for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { final TaskStack stack = mStackIdToStack.valueAt(stackNdx); @@ -9076,13 +9084,13 @@ public class WindowManagerService extends IWindowManager.Stub // If the screen is currently frozen or off, then keep // it frozen/off until this window draws at its new // orientation. - if (!okToDisplay()) { + if (!okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Changing surface while display frozen: " + w); w.mOrientationChanging = true; w.mLastFreezeDuration = 0; mInnerFields.mOrientationChangeComplete = false; - if (!mWindowsFreezingScreen) { - mWindowsFreezingScreen = true; + if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) { + mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; // XXX should probably keep timeout from // when we first froze the display. mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); @@ -10107,8 +10115,8 @@ public class WindowManagerService extends IWindowManager.Stub "With display frozen, orientationChangeComplete=" + mInnerFields.mOrientationChangeComplete); if (mInnerFields.mOrientationChangeComplete) { - if (mWindowsFreezingScreen) { - mWindowsFreezingScreen = false; + if (mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) { + mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE; mLastFinishedFreezeSource = mInnerFields.mLastWindowFreezeSource; mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); } @@ -10394,7 +10402,7 @@ public class WindowManagerService extends IWindowManager.Stub } else { mInnerFields.mOrientationChangeComplete = true; mInnerFields.mLastWindowFreezeSource = mAnimator.mLastWindowFreezeSource; - if (mWindowsFreezingScreen) { + if (mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) { doRequest = true; } } @@ -10739,7 +10747,8 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (mWaitingForConfig || mAppsFreezingScreen > 0 || mWindowsFreezingScreen + if (mWaitingForConfig || mAppsFreezingScreen > 0 + || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE || mClientFreezingScreen || !mOpeningApps.isEmpty()) { if (DEBUG_ORIENTATION) Slog.d(TAG, "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index c5495a5..9956bd7 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -67,7 +67,7 @@ aaptHostStaticLibs := \ libziparchive-host aaptCFlags := -DAAPT_VERSION=\"$(BUILD_NUMBER)\" -aaptCFLAGS += -Wall -Werror +aaptCFlags += -Wall -Werror ifeq ($(HOST_OS),linux) aaptHostLdLibs += -lrt -ldl -lpthread diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index 063b4e6..e4738f5 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -412,7 +412,6 @@ static void find_max_opacity(png_byte** rows, int startX, int startY, int endX, int endY, int dX, int dY, int* out_inset) { - bool opaque_within_inset = true; uint8_t max_opacity = 0; int inset = 0; *out_inset = 0; diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 38d10cf..beb94fd 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -3056,7 +3056,6 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) const sp<AaptDir>& d = dirs.itemAt(k); const String8& dirName = d->getLeaf(); Vector<String8> startTags; - const char* startTag = NULL; const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL; if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { tagAttrPairs = &kLayoutTagAttrPairs; diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 24f8168..c5fccbf 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -3167,7 +3167,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& if (!validResources[i]) { sp<ConfigList> c = t->getOrderedConfigs().itemAt(i); if (c != NULL) { - fprintf(stderr, "%s: no entries written for %s/%s (0x%08x)\n", log_prefix, + fprintf(stderr, "%s: no entries written for %s/%s (0x%08zx)\n", log_prefix, String8(typeName).string(), String8(c->getName()).string(), Res_MAKEID(p->getAssignedId() - 1, ti, i)); } @@ -4526,7 +4526,6 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle) { const KeyedVector<String16, Item>& bag = e->getBag(); const size_t bagCount = bag.size(); for (size_t bi = 0; bi < bagCount; bi++) { - const Item& item = bag.valueAt(bi); const uint32_t attrId = getResId(bag.keyAt(bi), &attr16); const int sdkLevel = getPublicAttributeSdkLevel(attrId); if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) { |