diff options
75 files changed, 2902 insertions, 2090 deletions
diff --git a/api/current.txt b/api/current.txt index 144f292..c21fa0a 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 @@ -16996,6 +16998,7 @@ package android.net { } public final class IpPrefix implements android.os.Parcelable { + method public boolean contains(java.net.InetAddress); method public int describeContents(); method public java.net.InetAddress getAddress(); method public int getPrefixLength(); @@ -17515,7 +17518,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 +41522,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..6732a09 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 @@ -18259,6 +18261,7 @@ package android.net { } public final class IpPrefix implements android.os.Parcelable { + method public boolean contains(java.net.InetAddress); method public int describeContents(); method public java.net.InetAddress getAddress(); method public int getPrefixLength(); @@ -18843,7 +18846,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 +44058,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..b063209 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; @@ -261,6 +261,8 @@ public final class ActivityThread { IActivityManager.ContentProviderHolder holder; boolean acquiring = true; int requests = 1; + // Set if there was a runtime exception when trying to acquire the provider. + RuntimeException runtimeException = null; } // The lock of mProviderMap protects the following variables. @@ -3187,7 +3189,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 +3487,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 +3920,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 +4120,7 @@ public final class ActivityThread { if (config == null) { return; } - + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: " + config); @@ -4166,7 +4168,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 +4249,7 @@ public final class ActivityThread { ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo); } - + final void handleLowMemory() { ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null); @@ -4294,10 +4296,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 @@ -4670,39 +4672,55 @@ public final class ActivityThread { } IActivityManager.ContentProviderHolder holder = null; - if (first) { - // Multiple threads may try to acquire the same provider at the same time. - // When this happens, we only let the first one really gets provider. - // Other threads just wait for its result. - // Note that we cannot hold the lock while acquiring and installing the - // provider since it might take a long time to run and it could also potentially - // be re-entrant in the case where the provider is in the same process. - try { + try { + if (first) { + // Multiple threads may try to acquire the same provider at the same time. + // When this happens, we only let the first one really gets provider. + // Other threads just wait for its result. + // Note that we cannot hold the lock while acquiring and installing the + // provider since it might take a long time to run and it could also potentially + // be re-entrant in the case where the provider is in the same process. holder = ActivityManagerNative.getDefault().getContentProvider( getApplicationThread(), auth, userId, stable); - } catch (RemoteException ex) { + } else { + synchronized (r) { + while (r.acquiring) { + try { + r.wait(); + } catch (InterruptedException e) { + } + } + holder = r.holder; + } } + } catch (RemoteException ex) { + } catch (RuntimeException e) { synchronized (r) { - r.holder = holder; - r.acquiring = false; - r.notifyAll(); + r.runtimeException = e; } - } else { - synchronized (r) { - while (r.acquiring) { - try { - r.wait(); - } catch (InterruptedException e) { - } + } finally { + if (first) { + synchronized (r) { + r.holder = holder; + r.acquiring = false; + r.notifyAll(); } - holder = r.holder; } - } - synchronized (mAcquiringProviderMap) { - if (--r.requests == 0) { - mAcquiringProviderMap.remove(key); + + synchronized (mAcquiringProviderMap) { + if (--r.requests == 0) { + mAcquiringProviderMap.remove(key); + } + } + + if (r.runtimeException != null) { + // Was set when the first thread tried to acquire the provider, + // but we should make sure it is thrown for all threads trying to + // acquire the provider. + throw r.runtimeException; } } + if (holder == null) { Slog.e(TAG, "Failed to find provider info for " + auth); return null; @@ -5222,7 +5240,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/net/IpPrefix.java b/core/java/android/net/IpPrefix.java index b268986..6b4f2d5 100644 --- a/core/java/android/net/IpPrefix.java +++ b/core/java/android/net/IpPrefix.java @@ -170,6 +170,21 @@ public final class IpPrefix implements Parcelable { } /** + * Determines whether the prefix contains the specified address. + * + * @param address An {@link InetAddress} to test. + * @return {@code true} if the prefix covers the given address. + */ + public boolean contains(InetAddress address) { + byte[] addrBytes = (address == null) ? null : address.getAddress(); + if (addrBytes == null || addrBytes.length != this.address.length) { + return false; + } + NetworkUtils.maskRawAddress(addrBytes, prefixLength); + return Arrays.equals(this.address, addrBytes); + } + + /** * Returns a string representation of this {@code IpPrefix}. * * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::/64"}. diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index cfd20a0..90a2460 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -367,13 +367,7 @@ public final class RouteInfo implements Parcelable { * @return {@code true} if the destination and prefix length cover the given address. */ public boolean matches(InetAddress destination) { - if (destination == null) return false; - - // match the route destination and destination with prefix length - InetAddress dstNet = NetworkUtils.getNetworkPart(destination, - mDestination.getPrefixLength()); - - return mDestination.getAddress().equals(dstNet); + return mDestination.contains(destination); } /** diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 2d92c7b..1c85db4 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -34,6 +34,7 @@ import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Map; import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; @@ -1035,6 +1036,95 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo } /** + * Returns the value of a particular runtime statistic or {@code null} if no + * such runtime statistic exists. + * + * <p>The following table lists the runtime statistics that the runtime supports. + * Note runtime statistics may be added or removed in a future API level.</p> + * + * <table> + * <thead> + * <tr> + * <th>Runtime statistic name</th> + * <th>Meaning</th> + * <th>Example</th> + * <th>Supported (API Levels)</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td>art.gc.gc-count</td> + * <td>The number of garbage collection runs.</td> + * <td>{@code 164}</td> + * <td>23</td> + * </tr> + * <tr> + * <td>art.gc.gc-time</td> + * <td>The total duration of garbage collection runs in ms.</td> + * <td>{@code 62364}</td> + * <td>23</td> + * </tr> + * <tr> + * <td>art.gc.bytes-allocated</td> + * <td>The total number of bytes that the application allocated.</td> + * <td>{@code 1463948408}</td> + * <td>23</td> + * </tr> + * <tr> + * <td>art.gc.bytes-freed</td> + * <td>The total number of bytes that garbage collection reclaimed.</td> + * <td>{@code 1313493084}</td> + * <td>23</td> + * </tr> + * <tr> + * <td>art.gc.blocking-gc-count</td> + * <td>The number of blocking garbage collection runs.</td> + * <td>{@code 2}</td> + * <td>23</td> + * </tr> + * <tr> + * <td>art.gc.blocking-gc-time</td> + * <td>The total duration of blocking garbage collection runs in ms.</td> + * <td>{@code 804}</td> + * <td>23</td> + * </tr> + * <tr> + * <td>art.gc.gc-count-rate-histogram</td> + * <td>The histogram of the number of garbage collection runs per 10 seconds.</td> + * <td>{@code 0:34503,1:45350,2:11281,3:8088,4:43,5:8}</td> + * <td>23</td> + * </tr> + * <tr> + * <td>art.gc.blocking-gc-count-rate-histogram</td> + * <td>The histogram of the number of garbage collection runs per 10 seconds.</td> + * <td>{@code 0:99269,1:1,2:1}</td> + * <td>23</td> + * </tr> + * </tbody> + * </table> + * + * @param statName + * the name of the runtime statistic to look up. + * @return the value of the specified runtime statistic or {@code null} if the + * runtime statistic doesn't exist. + * @hide + */ + public static String getRuntimeStat(String statName) { + return VMDebug.getRuntimeStat(statName); + } + + /** + * Returns a map of the names/values of the runtime statistics + * that {@link #getRuntimeStat()} supports. + * + * @return a map of the names/values of the supported runtime statistics. + * @hide + */ + public static Map<String, String> getRuntimeStats() { + return VMDebug.getRuntimeStats(); + } + + /** * Returns the size of the native heap. * @return The size of the native heap in bytes. */ diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java index b803a1b..b3a3aad 100644 --- a/core/java/android/security/keymaster/KeyCharacteristics.java +++ b/core/java/android/security/keymaster/KeyCharacteristics.java @@ -19,6 +19,8 @@ package android.security.keymaster; import android.os.Parcel; import android.os.Parcelable; +import java.util.ArrayList; +import java.util.Date; import java.util.List; /** @@ -30,10 +32,12 @@ public class KeyCharacteristics implements Parcelable { public static final Parcelable.Creator<KeyCharacteristics> CREATOR = new Parcelable.Creator<KeyCharacteristics>() { + @Override public KeyCharacteristics createFromParcel(Parcel in) { return new KeyCharacteristics(in); } + @Override public KeyCharacteristics[] newArray(int length) { return new KeyCharacteristics[length]; } @@ -50,6 +54,7 @@ public class KeyCharacteristics implements Parcelable { return 0; } + @Override public void writeToParcel(Parcel out, int flags) { swEnforced.writeToParcel(out, flags); hwEnforced.writeToParcel(out, flags); @@ -59,5 +64,53 @@ public class KeyCharacteristics implements Parcelable { swEnforced = KeymasterArguments.CREATOR.createFromParcel(in); hwEnforced = KeymasterArguments.CREATOR.createFromParcel(in); } + + public Integer getInteger(int tag) { + if (hwEnforced.containsTag(tag)) { + return hwEnforced.getInt(tag, -1); + } else if (swEnforced.containsTag(tag)) { + return swEnforced.getInt(tag, -1); + } else { + return null; + } + } + + public int getInt(int tag, int defaultValue) { + Integer result = getInteger(tag); + return (result != null) ? result : defaultValue; + } + + public List<Integer> getInts(int tag) { + List<Integer> result = new ArrayList<Integer>(); + result.addAll(hwEnforced.getInts(tag)); + result.addAll(swEnforced.getInts(tag)); + return result; + } + + public Date getDate(int tag) { + Date result = hwEnforced.getDate(tag, null); + if (result == null) { + result = swEnforced.getDate(tag, null); + } + return result; + } + + public Date getDate(int tag, Date defaultValue) { + if (hwEnforced.containsTag(tag)) { + return hwEnforced.getDate(tag, null); + } else if (hwEnforced.containsTag(tag)) { + return swEnforced.getDate(tag, null); + } else { + return defaultValue; + } + } + + public boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) { + if (keyCharacteristics.hwEnforced.containsTag(tag)) { + return keyCharacteristics.hwEnforced.getBoolean(tag, false); + } else { + return keyCharacteristics.swEnforced.getBoolean(tag, false); + } + } } diff --git a/core/java/android/security/keymaster/KeymasterArguments.java b/core/java/android/security/keymaster/KeymasterArguments.java index b5fd4bd..8ed288c 100644 --- a/core/java/android/security/keymaster/KeymasterArguments.java +++ b/core/java/android/security/keymaster/KeymasterArguments.java @@ -34,9 +34,12 @@ public class KeymasterArguments implements Parcelable { public static final Parcelable.Creator<KeymasterArguments> CREATOR = new Parcelable.Creator<KeymasterArguments>() { + @Override public KeymasterArguments createFromParcel(Parcel in) { return new KeymasterArguments(in); } + + @Override public KeymasterArguments[] newArray(int size) { return new KeymasterArguments[size]; } @@ -54,6 +57,12 @@ public class KeymasterArguments implements Parcelable { mArguments.add(new KeymasterIntArgument(tag, value)); } + public void addInts(int tag, int... values) { + for (int value : values) { + addInt(tag, value); + } + } + public void addBoolean(int tag) { mArguments.add(new KeymasterBooleanArgument(tag)); } diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index e94a312..d3953b3 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -47,21 +47,17 @@ public final class KeymasterDefs { public static final int KM_TAG_PURPOSE = KM_ENUM_REP | 1; public static final int KM_TAG_ALGORITHM = KM_ENUM | 2; public static final int KM_TAG_KEY_SIZE = KM_INT | 3; - public static final int KM_TAG_BLOCK_MODE = KM_ENUM | 4; - public static final int KM_TAG_DIGEST = KM_ENUM | 5; - public static final int KM_TAG_MAC_LENGTH = KM_INT | 6; - public static final int KM_TAG_PADDING = KM_ENUM | 7; - public static final int KM_TAG_RETURN_UNAUTHED = KM_BOOL | 8; - public static final int KM_TAG_CALLER_NONCE = KM_BOOL | 9; + public static final int KM_TAG_BLOCK_MODE = KM_ENUM_REP | 4; + public static final int KM_TAG_DIGEST = KM_ENUM_REP | 5; + public static final int KM_TAG_PADDING = KM_ENUM_REP | 6; + public static final int KM_TAG_RETURN_UNAUTHED = KM_BOOL | 7; + public static final int KM_TAG_CALLER_NONCE = KM_BOOL | 8; public static final int KM_TAG_RESCOPING_ADD = KM_ENUM_REP | 101; public static final int KM_TAG_RESCOPING_DEL = KM_ENUM_REP | 102; public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705; public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_LONG | 200; - public static final int KM_TAG_DSA_GENERATOR = KM_BIGNUM | 201; - public static final int KM_TAG_DSA_P = KM_BIGNUM | 202; - public static final int KM_TAG_DSA_Q = KM_BIGNUM | 203; public static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400; public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401; public static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402; @@ -88,43 +84,21 @@ public final class KeymasterDefs { public static final int KM_TAG_NONCE = KM_BYTES | 1001; public static final int KM_TAG_CHUNK_LENGTH = KM_INT | 1002; public static final int KM_TAG_AUTH_TOKEN = KM_BYTES | 1003; + public static final int KM_TAG_MAC_LENGTH = KM_INT | 1004; // Algorithm values. public static final int KM_ALGORITHM_RSA = 1; - public static final int KM_ALGORITHM_DSA = 2; - public static final int KM_ALGORITHM_ECDSA = 3; - public static final int KM_ALGORITHM_ECIES = 4; + public static final int KM_ALGORITHM_EC = 3; public static final int KM_ALGORITHM_AES = 32; - public static final int KM_ALGORITHM_3DES = 33; - public static final int KM_ALGORITHM_SKIPJACK = 34; - public static final int KM_ALGORITHM_MARS = 48; - public static final int KM_ALGORITHM_RC6 = 49; - public static final int KM_ALGORITHM_SERPENT = 50; - public static final int KM_ALGORITHM_TWOFISH = 51; - public static final int KM_ALGORITHM_IDEA = 52; - public static final int KM_ALGORITHM_RC5 = 53; - public static final int KM_ALGORITHM_CAST5 = 54; - public static final int KM_ALGORITHM_BLOWFISH = 55; - public static final int KM_ALGORITHM_RC4 = 64; - public static final int KM_ALGORITHM_CHACHA20 = 65; public static final int KM_ALGORITHM_HMAC = 128; // Block modes. public static final int KM_MODE_FIRST_UNAUTHENTICATED = 1; public static final int KM_MODE_ECB = KM_MODE_FIRST_UNAUTHENTICATED; public static final int KM_MODE_CBC = 2; - public static final int KM_MODE_CBC_CTS = 3; public static final int KM_MODE_CTR = 4; - public static final int KM_MODE_OFB = 5; - public static final int KM_MODE_CFB = 6; - public static final int KM_MODE_XTS = 7; public static final int KM_MODE_FIRST_AUTHENTICATED = 32; public static final int KM_MODE_GCM = KM_MODE_FIRST_AUTHENTICATED; - public static final int KM_MODE_OCB = 33; - public static final int KM_MODE_CCM = 34; - public static final int KM_MODE_FIRST_MAC = 128; - public static final int KM_MODE_CMAC = KM_MODE_FIRST_MAC; - public static final int KM_MODE_POLY1305 = 129; // Padding modes. public static final int KM_PAD_NONE = 1; @@ -132,11 +106,7 @@ public final class KeymasterDefs { public static final int KM_PAD_RSA_PSS = 3; public static final int KM_PAD_RSA_PKCS1_1_5_ENCRYPT = 4; public static final int KM_PAD_RSA_PKCS1_1_5_SIGN = 5; - public static final int KM_PAD_ANSI_X923 = 32; - public static final int KM_PAD_ISO_10126 = 33; - public static final int KM_PAD_ZERO = 64; - public static final int KM_PAD_PKCS7 = 65; - public static final int KM_PAD_ISO_7816_4 = 66; + public static final int KM_PAD_PKCS7 = 64; // Digest modes. public static final int KM_DIGEST_NONE = 0; @@ -146,9 +116,6 @@ public final class KeymasterDefs { public static final int KM_DIGEST_SHA_2_256 = 4; public static final int KM_DIGEST_SHA_2_384 = 5; public static final int KM_DIGEST_SHA_2_512 = 6; - public static final int KM_DIGEST_SHA_3_256 = 7; - public static final int KM_DIGEST_SHA_3_384 = 8; - public static final int KM_DIGEST_SHA_3_512 = 9; // Key origins. public static final int KM_ORIGIN_HARDWARE = 0; @@ -168,9 +135,11 @@ public final class KeymasterDefs { // Key formats. public static final int KM_KEY_FORMAT_X509 = 0; public static final int KM_KEY_FORMAT_PKCS8 = 1; - 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; @@ -215,7 +184,6 @@ public final class KeymasterDefs { public static final int KM_ERROR_INVALID_TAG = -40; public static final int KM_ERROR_MEMORY_ALLOCATION_FAILED = -41; public static final int KM_ERROR_INVALID_RESCOPING = -42; - public static final int KM_ERROR_INVALID_DSA_PARAMS = -43; public static final int KM_ERROR_IMPORT_PARAMETER_MISMATCH = -44; public static final int KM_ERROR_SECURE_HW_ACCESS_DENIED = -45; public static final int KM_ERROR_OPERATION_CANCELLED = -46; 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/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index b3bafa1..1038acf 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -256,7 +256,7 @@ public class PowerProfile { final Double[] values = (Double[]) data; if (values.length > level && level >= 0) { return values[level]; - } else if (level < 0) { + } else if (level < 0 || values.length == 0) { return 0; } else { return values[values.length - 1]; 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/core/tests/coretests/src/android/net/IpPrefixTest.java b/core/tests/coretests/src/android/net/IpPrefixTest.java index cf278fb..fcc6389 100644 --- a/core/tests/coretests/src/android/net/IpPrefixTest.java +++ b/core/tests/coretests/src/android/net/IpPrefixTest.java @@ -29,6 +29,10 @@ import junit.framework.TestCase; public class IpPrefixTest extends TestCase { + private static InetAddress Address(String addr) { + return InetAddress.parseNumericAddress(addr); + } + // Explicitly cast everything to byte because "error: possible loss of precision". private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4}; private static final byte[] IPV6_BYTES = { @@ -209,6 +213,34 @@ public class IpPrefixTest extends TestCase { } @SmallTest + public void testContains() { + IpPrefix p = new IpPrefix("2001:db8:f00::ace:d00d/127"); + assertTrue(p.contains(Address("2001:db8:f00::ace:d00c"))); + assertTrue(p.contains(Address("2001:db8:f00::ace:d00d"))); + assertFalse(p.contains(Address("2001:db8:f00::ace:d00e"))); + assertFalse(p.contains(Address("2001:db8:f00::bad:d00d"))); + assertFalse(p.contains(Address("2001:4868:4860::8888"))); + assertFalse(p.contains(null)); + assertFalse(p.contains(Address("8.8.8.8"))); + + p = new IpPrefix("192.0.2.0/23"); + assertTrue(p.contains(Address("192.0.2.43"))); + assertTrue(p.contains(Address("192.0.3.21"))); + assertFalse(p.contains(Address("192.0.0.21"))); + assertFalse(p.contains(Address("8.8.8.8"))); + assertFalse(p.contains(Address("2001:4868:4860::8888"))); + + IpPrefix ipv6Default = new IpPrefix("::/0"); + assertTrue(ipv6Default.contains(Address("2001:db8::f00"))); + assertFalse(ipv6Default.contains(Address("192.0.2.1"))); + + IpPrefix ipv4Default = new IpPrefix("0.0.0.0/0"); + assertTrue(ipv4Default.contains(Address("255.255.255.255"))); + assertTrue(ipv4Default.contains(Address("192.0.2.1"))); + assertFalse(ipv4Default.contains(Address("2001:db8::f00"))); + } + + @SmallTest public void testHashCode() { IpPrefix p; int oldCode = -1; diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/core/tests/coretests/src/android/net/RouteInfoTest.java index 0b88bc7..831fefd 100644 --- a/core/tests/coretests/src/android/net/RouteInfoTest.java +++ b/core/tests/coretests/src/android/net/RouteInfoTest.java @@ -90,6 +90,7 @@ public class RouteInfoTest extends TestCase { assertFalse(r.matches(Address("2001:db8:f00::ace:d00e"))); assertFalse(r.matches(Address("2001:db8:f00::bad:d00d"))); assertFalse(r.matches(Address("2001:4868:4860::8888"))); + assertFalse(r.matches(Address("8.8.8.8"))); r = new PatchedRouteInfo(Prefix("192.0.2.0/23"), null, "wlan0"); assertTrue(r.matches(Address("192.0.2.43"))); diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index ff2c6d1..8de0138 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -112,7 +112,7 @@ struct __assertChar16Size { * * The PNG chunk type is "npTc". */ -struct Res_png_9patch +struct alignas(uintptr_t) Res_png_9patch { Res_png_9patch() : wasDeserialized(false), xDivsOffset(0), yDivsOffset(0), colorsOffset(0) { } diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index dcc79be..fcee9fc 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -19,6 +19,8 @@ package android.security; import com.android.org.conscrypt.OpenSSLEngine; import com.android.org.conscrypt.OpenSSLKeyHolder; +import libcore.util.EmptyArray; + import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterDefs; @@ -46,6 +48,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -112,13 +115,6 @@ public class AndroidKeyStore extends KeyStoreSpi { if (keymasterAlgorithm == -1) { throw new UnrecoverableKeyException("Key algorithm unknown"); } - @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm; - try { - keyAlgorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(keymasterAlgorithm); - } catch (IllegalArgumentException e) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Unsupported key algorithm").initCause(e); - } int keymasterDigest = keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1); @@ -126,20 +122,11 @@ public class AndroidKeyStore extends KeyStoreSpi { keymasterDigest = keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1); } - @KeyStoreKeyConstraints.DigestEnum Integer digest = null; - if (keymasterDigest != -1) { - try { - digest = KeyStoreKeyConstraints.Digest.fromKeymaster(keymasterDigest); - } catch (IllegalArgumentException e) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Unsupported digest").initCause(e); - } - } String keyAlgorithmString; try { - keyAlgorithmString = KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm( - keyAlgorithm, digest); + keyAlgorithmString = KeymasterUtils.getJcaSecretKeyAlgorithm( + keymasterAlgorithm, keymasterDigest); } catch (IllegalArgumentException e) { throw (UnrecoverableKeyException) new UnrecoverableKeyException("Unsupported secret key type").initCause(e); @@ -456,91 +443,95 @@ public class AndroidKeyStore extends KeyStoreSpi { } String keyAlgorithmString = key.getAlgorithm(); - @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm; - @KeyStoreKeyConstraints.DigestEnum Integer digest; + int keymasterAlgorithm; + int keymasterDigest; try { - keyAlgorithm = - KeyStoreKeyConstraints.Algorithm.fromJCASecretKeyAlgorithm(keyAlgorithmString); - digest = KeyStoreKeyConstraints.Digest.fromJCASecretKeyAlgorithm(keyAlgorithmString); + keymasterAlgorithm = KeymasterUtils.getKeymasterAlgorithmFromJcaSecretKeyAlgorithm( + keyAlgorithmString); + keymasterDigest = + KeymasterUtils.getKeymasterDigestfromJcaSecretKeyAlgorithm(keyAlgorithmString); } catch (IllegalArgumentException e) { 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 - + ", parameter spec: " - + KeyStoreKeyConstraints.Digest.toString(params.getDigest())); + args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm); + + int[] keymasterDigests; + if (params.isDigestsSpecified()) { + // Digest(s) specified in parameters + keymasterDigests = + KeymasterUtils.getKeymasterDigestsFromJcaDigestAlgorithms(params.getDigests()); + if (keymasterDigest != -1) { + // Digest also specified in the JCA key algorithm name. + if (!com.android.internal.util.ArrayUtils.contains( + keymasterDigests, keymasterDigest)) { + throw new KeyStoreException("Key digest mismatch" + + ". Key: " + keyAlgorithmString + + ", parameter spec: " + Arrays.asList(params.getDigests())); } } } else { - // Digest not available from JCA key algorithm - digest = params.getDigest(); - } - if (digest != null) { - args.addInt(KeymasterDefs.KM_TAG_DIGEST, - KeyStoreKeyConstraints.Digest.toKeymaster(digest)); - Integer digestOutputSizeBytes = - KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest); - if (digestOutputSizeBytes != null) { - // TODO: Remove MAC length constraint once Keymaster API no longer requires it. + // No digest specified in parameters + if (keymasterDigest != -1) { + // Digest specified in the JCA key algorithm name. + keymasterDigests = new int[] {keymasterDigest}; + } else { + keymasterDigests = EmptyArray.INT; + } + } + args.addInts(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests); + if (keymasterDigests.length > 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. + int digestOutputSizeBytes = + KeymasterUtils.getDigestOutputSizeBytes(keymasterDigests[0]); + if (digestOutputSizeBytes != -1) { // 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 (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { + if (keymasterDigests.length == 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())); - } - if (params.getPadding() != null) { - args.addInt(KeymasterDefs.KM_TAG_PADDING, - KeyStoreKeyConstraints.Padding.toKeymaster(params.getPadding())); - } - if (params.getMaxUsesPerBoot() != null) { - args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, params.getMaxUsesPerBoot()); + @KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes(); + int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes( + params.getBlockModes()); + if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) + && (params.isRandomizedEncryptionRequired())) { + for (int keymasterBlockMode : keymasterBlockModes) { + if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) { + throw new KeyStoreException( + "Randomized encryption (IND-CPA) required but may be violated by block" + + " mode: " + + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode( + keymasterBlockMode) + + ". See KeyStoreParameter documentation."); + } + } } - if (params.getMinSecondsBetweenOperations() != null) { - args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS, - params.getMinSecondsBetweenOperations()); + for (int keymasterPurpose : KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) { + args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); } - if (params.getUserAuthenticators().isEmpty()) { + args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes); + int[] keymasterPaddings = ArrayUtils.concat( + KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings( + params.getEncryptionPaddings()), + KeymasterUtils.getKeymasterPaddingsFromJcaSignaturePaddings( + params.getSignaturePaddings())); + args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings); + if (params.getUserAuthenticators() == 0) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); } else { args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, - KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster( + KeyStoreKeyProperties.UserAuthenticator.allToKeymaster( params.getUserAuthenticators())); } - if (params.getUserAuthenticationValidityDurationSeconds() != null) { + if (params.getUserAuthenticationValidityDurationSeconds() != -1) { args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, params.getUserAuthenticationValidityDurationSeconds()); } @@ -557,9 +548,9 @@ public class AndroidKeyStore extends KeyStoreSpi { // TODO: Remove this once keymaster does not require us to specify the size of imported key. 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. + if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) + && (!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..43f3b30 100644 --- a/keystore/java/android/security/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/AndroidKeyStoreProvider.java @@ -49,14 +49,26 @@ 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("HmacSHA1", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA1"); + 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 +85,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/ArrayUtils.java b/keystore/java/android/security/ArrayUtils.java new file mode 100644 index 0000000..2047d3f --- /dev/null +++ b/keystore/java/android/security/ArrayUtils.java @@ -0,0 +1,62 @@ +package android.security; + +import libcore.util.EmptyArray; + +/** + * @hide + */ +abstract class ArrayUtils { + private ArrayUtils() {} + + public static String[] nullToEmpty(String[] array) { + return (array != null) ? array : EmptyArray.STRING; + } + + public static String[] cloneIfNotEmpty(String[] array) { + return ((array != null) && (array.length > 0)) ? array.clone() : array; + } + + public static byte[] concat(byte[] arr1, byte[] arr2) { + return concat(arr1, 0, (arr1 != null) ? arr1.length : 0, + arr2, 0, (arr2 != null) ? arr2.length : 0); + } + + public static byte[] concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2, + int len2) { + if (len1 == 0) { + return subarray(arr2, offset2, len2); + } else if (len2 == 0) { + return subarray(arr1, offset1, len1); + } else { + byte[] result = new byte[len1 + len2]; + System.arraycopy(arr1, offset1, result, 0, len1); + System.arraycopy(arr2, offset2, result, len1, len2); + return result; + } + } + + public static byte[] subarray(byte[] arr, int offset, int len) { + if (len == 0) { + return EmptyArray.BYTE; + } + if ((offset == 0) && (len == arr.length)) { + return arr; + } + byte[] result = new byte[len]; + System.arraycopy(arr, offset, result, 0, len); + return result; + } + + public static int[] concat(int[] arr1, int[] arr2) { + if ((arr1 == null) || (arr1.length == 0)) { + return arr2; + } else if ((arr2 == null) || (arr2.length == 0)) { + return arr1; + } else { + int[] result = new int[arr1.length + arr2.length]; + System.arraycopy(arr1, 0, result, 0, arr1.length); + System.arraycopy(arr2, 0, result, arr1.length, arr2.length); + return result; + } + } +} diff --git a/keystore/java/android/security/EcIesParameterSpec.java b/keystore/java/android/security/EcIesParameterSpec.java new file mode 100644 index 0000000..3372da9 --- /dev/null +++ b/keystore/java/android/security/EcIesParameterSpec.java @@ -0,0 +1,264 @@ +package android.security; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.Cipher; +import javax.crypto.Mac; + +/** + * {@link AlgorithmParameterSpec} for ECIES (Integrated Encryption Scheme using Elliptic Curve + * cryptography) based on {@code ISO/IEC 18033-2}. + * + * <p>ECIES is a hybrid authenticated encryption scheme. Encryption is performed using an Elliptic + * Curve (EC) public key. The resulting ciphertext can be decrypted only using the corresponding EC + * private key. The scheme is called hybrid because the EC key is only used to securely encapsulate + * symmetric key material. Encryption of plaintext and authentication of the corresponding + * ciphertext is performed using symmetric cryptography. + * + * <p>Encryption using ECIES consists of two stages: + * <ol> + * <li>Key Encapsulation Mechanism (KEM) randomly generates symmetric key material and securely + * encapsulates it in the output so that it can be extracted by the KEM when decrypting. + * Encapsulated key material is represented in the output as an EC point.</li> + * <li>The above symmetric key material is used by Data Encapsulation Mechanism (DEM) to encrypt the + * provided plaintext and authenticate the ciphertext. The resulting authenticated ciphertext is + * then output. When decrypting, the DEM first authenticates the ciphertext and, only if it + * authenticates, decrypts the ciphertext and outputs the plaintext.</li> + * </ol> + * + * <p>Details of KEM: + * <ul> + * <li>Only curves with cofactor of {@code 1} are supported.</li> + * <li>{@code CheckMode}, {@code OldCofactorMode}, {@code CofactorMode}, and {@code SingleHashMode} + * are {@code 0}. + * <li>Point format is specified by {@link #getKemPointFormat()}.</li> + * <li>KDF algorithm is specified by {@link #getKemKdfAlgorithm()}.</li> + * </ul> + * + * <p>Details of DEM: + * <ul> + * <li>Only DEM1-like mechanism is supported, with its symmetric cipher (SC) specified by + * {@link #getDemCipherTransformation()} (e.g., {@code AES/CBC/NoPadding} for standard DEM1) and + * MAC algorithm specified by {@link #getDemMacAlgorithm()} (e.g., {@code HmacSHA1} for standard + * DEM1).</li> + * </ul> + * + * @hide + */ +public class EcIesParameterSpec implements AlgorithmParameterSpec { + + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = {PointFormat.UNCOMPRESSED, PointFormat.COMPRESSED}) + public @interface PointFormatEnum {} + + /** + * Wire format of the EC point. + */ + public static abstract class PointFormat { + + private PointFormat() {} + + /** Unspecified point format. */ + public static final int UNSPECIFIED = -1; + + /** + * Uncompressed point format: both coordinates are stored separately. + * + * <p>The wire format is byte {@code 0x04} followed by binary representation of the + * {@code x} coordinate followed by binary representation of the {@code y} coordinate. See + * {@code ISO 18033-2} section {@code 5.4.3}. + */ + public static final int UNCOMPRESSED = 0; + + /** + * Compressed point format: only one coordinate is stored. + * + * <p>The wire format is byte {@code 0x02} or {@code 0x03} (depending on the value of the + * stored coordinate) followed by the binary representation of the {@code x} coordinate. + * See {@code ISO 18033-2} section {@code 5.4.3}. + */ + public static final int COMPRESSED = 1; + } + + /** + * Default parameter spec: compressed point format, {@code HKDFwithSHA256}, DEM uses 128-bit AES + * GCM. + */ + public static final EcIesParameterSpec DEFAULT = new EcIesParameterSpec( + PointFormat.COMPRESSED, + "HKDFwithSHA256", + "AES/GCM/NoPadding", + 128, + null, + 0); + + private final @PointFormatEnum int mKemPointFormat; + private final String mKemKdfAlgorithm; + private final String mDemCipherTransformation; + private final int mDemCipherKeySize; + private final String mDemMacAlgorithm; + private final int mDemMacKeySize; + + private EcIesParameterSpec( + @PointFormatEnum int kemPointFormat, + String kemKdfAlgorithm, + String demCipherTransformation, + int demCipherKeySize, + String demMacAlgorithm, + int demMacKeySize) { + mKemPointFormat = kemPointFormat; + mKemKdfAlgorithm = kemKdfAlgorithm; + mDemCipherTransformation = demCipherTransformation; + mDemCipherKeySize = demCipherKeySize; + mDemMacAlgorithm = demMacAlgorithm; + mDemMacKeySize = demMacKeySize; + } + + /** + * Returns KEM EC point wire format or {@link PointFormat#UNSPECIFIED} if not specified. + */ + public @PointFormatEnum int getKemPointFormat() { + return mKemPointFormat; + } + + /** + * Returns KEM KDF algorithm (e.g., {@code HKDFwithSHA256} or {@code KDF1withSHA1}) or + * {@code null} if not specified. + */ + public String getKemKdfAlgorithm() { + return mKemKdfAlgorithm; + } + + /** + * Returns DEM {@link Cipher} transformation (e.g., {@code AES/GCM/NoPadding} or + * {@code AES/CBC/PKCS7Padding}) or {@code null} if not specified. + * + * @see Cipher#getInstance(String) + * @see #getDemCipherKeySize() + */ + public String getDemCipherTransformation() { + return mDemCipherTransformation; + } + + /** + * Returns DEM {@link Cipher} key size in bits. + * + * @see #getDemCipherTransformation() + */ + public int getDemCipherKeySize() { + return mDemCipherKeySize; + } + + /** + * Returns DEM {@link Mac} algorithm (e.g., {@code HmacSHA256} or {@code HmacSHA1}) or + * {@code null} if not specified. + * + * @see Mac#getInstance(String) + * @see #getDemMacKeySize() + */ + public String getDemMacAlgorithm() { + return mDemMacAlgorithm; + } + + /** + * Returns DEM {@link Mac} key size in bits. + * + * @see #getDemCipherTransformation() + */ + public int getDemMacKeySize() { + return mDemMacKeySize; + } + + /** + * Builder of {@link EcIesParameterSpec}. + */ + public static class Builder { + private @PointFormatEnum int mKemPointFormat = PointFormat.UNSPECIFIED; + private String mKemKdfAlgorithm; + private String mDemCipherTransformation; + private int mDemCipherKeySize = 128; + private String mDemMacAlgorithm; + private int mDemMacKeySize = -1; + + /** + * Sets KEM EC point wire format. + */ + public Builder setKemPointFormat(@PointFormatEnum int pointFormat) { + mKemPointFormat = pointFormat; + return this; + } + + /** + * Sets KEM KDF algorithm. For example, {@code HKDFwithSHA256}, {@code KDF2withSHA256}, or + * {@code KDF1withSHA1}. + */ + public Builder setKemKdfAlgorithm(String algorithm) { + mKemKdfAlgorithm = algorithm; + return this; + } + + /** + * Sets DEM {@link Cipher} transformation. For example, {@code AES/GCM/NoPadding}, + * {@code AES/CBC/PKCS7Padding} or {@code AES/CTR/NoPadding}. + * + * @see Cipher#getInstance(String) + */ + public Builder setDemCipherTransformation(String transformation) { + mDemCipherTransformation = transformation; + return this; + } + + /** + * Returns DEM {@link Cipher} key size in bits. + * + * <p>The default value is {@code 128} bits. + * + * @see #setDemCipherTransformation(String) + */ + public Builder setDemCipherKeySize(int sizeBits) { + mDemCipherKeySize = sizeBits; + return this; + } + + /** + * Sets DEM {@link Mac} algorithm. For example, {@code HmacSHA256} or {@code HmacSHA1}. + * + * @see Mac#getInstance(String) + */ + public Builder setDemMacAlgorithm(String algorithm) { + mDemMacAlgorithm = algorithm; + return this; + } + + /** + * Sets DEM {@link Mac} key size in bits. + * + * <p>By default, {@code Mac} key size is the same as the {@code Cipher} key size. + * + * @see #setDemCipherKeySize(int) + */ + public Builder setDemMacKeySize(int sizeBits) { + mDemMacKeySize = sizeBits; + return this; + } + + /** + * Returns a new {@link EcIesParameterSpec} based on the current state of this builder. + */ + public EcIesParameterSpec build() { + int demMacKeySize = (mDemMacKeySize != -1) ? mDemMacKeySize : mDemCipherKeySize; + return new EcIesParameterSpec( + mKemPointFormat, + mKemKdfAlgorithm, + mDemCipherTransformation, + mDemCipherKeySize, + mDemMacAlgorithm, + demMacKeySize + ); + } + } +} diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java index 1311368..a22c31c 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 @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private final String[] mEncryptionPaddings; + private final String[] mBlockModes; + private final boolean mRandomizedEncryptionRequired; + private final @KeyStoreKeyProperties.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) { + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] encryptionPaddings, + String[] blockModes, + boolean randomizedEncryptionRequired, + @KeyStoreKeyProperties.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,11 @@ 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(); + mEncryptionPaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); + mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); + mRandomizedEncryptionRequired = randomizedEncryptionRequired; + mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -145,8 +138,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 +154,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 @KeyStoreKeyProperties.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 with which the key can be used when encrypting/decrypting. */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; + public String[] getEncryptionPaddings() { + return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** - * 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 with which the key can be used. */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; + public String[] getBlockModes() { + return ArrayUtils.cloneIfNotEmpty(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 @KeyStoreKeyProperties.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 +222,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 @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private String[] mEncryptionPaddings; + private String[] mBlockModes; + private boolean mRandomizedEncryptionRequired = true; + private @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; + private int mUserAuthenticationValidityDurationSeconds = -1; /** * Creates a new instance of the {@code Builder} with the given {@code context}. The @@ -315,11 +283,9 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant before which the key is not yet valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityEnd(Date) - * - * @hide */ public Builder setKeyValidityStart(Date startDate) { mKeyValidityStart = startDate; @@ -329,13 +295,11 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant after which the key is no longer valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityStart(Date) * @see #setKeyValidityForConsumptionEnd(Date) * @see #setKeyValidityForOriginationEnd(Date) - * - * @hide */ public Builder setKeyValidityEnd(Date endDate) { setKeyValidityForOriginationEnd(endDate); @@ -346,11 +310,9 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant after which the key is no longer valid for encryption and signing. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForConsumptionEnd(Date) - * - * @hide */ public Builder setKeyValidityForOriginationEnd(Date endDate) { mKeyValidityForOriginationEnd = endDate; @@ -361,11 +323,9 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * Sets the time instant after which the key is no longer valid for decryption and * verification. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForOriginationEnd(Date) - * - * @hide */ public Builder setKeyValidityForConsumptionEnd(Date endDate) { mKeyValidityForConsumptionEnd = endDate; @@ -373,65 +333,72 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** - * Restricts the purposes for which the key can be used to the provided set of purposes. + * Sets the set of purposes for which the key can be used. * - * <p>By default, the key can be used for encryption, decryption, signing, and verification. - * - * @hide + * <p>This must be specified for all keys. There is no default. */ - public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) { + public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; return this; } /** - * Restricts the key to being used only with the provided padding scheme. 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. + * Sets the set of padding schemes with which the key can be used when + * encrypting/decrypting. Attempts to use the key with any other padding scheme will be + * rejected. * - * @hide + * <p>This must be specified for keys which are used for encryption/decryption. */ - public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) { - mPadding = padding; + public Builder setEncryptionPaddings(String... paddings) { + mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(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. - * - * <p>This restriction must be specified for keys which are used for encryption/decryption. + * Sets the set of block modes with which the key can be used when encrypting/decrypting. + * Attempts to use the key with any other block modes will be rejected. * - * @hide + * <p>This must be specified for encryption/decryption keys. */ - public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) { - mBlockMode = blockMode; + public Builder setBlockModes(String... blockModes) { + mBlockModes = ArrayUtils.cloneIfNotEmpty(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 +412,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( + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators) { + mUserAuthenticators = userAuthenticators; return this; } @@ -463,9 +428,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 +441,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, + mEncryptionPaddings, + mBlockModes, + mRandomizedEncryptionRequired, + mUserAuthenticators, + mUserAuthenticationValidityDurationSeconds); } } } diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index 0001604..fce02df 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; @@ -55,6 +52,11 @@ import javax.security.auth.x500.X500Principal; */ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { + private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake"); + private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1"); + private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970 + private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048 + private final Context mContext; private final String mKeystoreAlias; @@ -81,21 +83,21 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private final Date mKeyValidityForConsumptionEnd; - private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; + private final @KeyStoreKeyProperties.PurposeEnum int mPurposes; - private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest; + private final String[] mDigests; - private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; + private final String[] mEncryptionPaddings; - private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; + private final String[] mSignaturePaddings; - private final Integer mMinSecondsBetweenOperations; + private final String[] mBlockModes; - private final Integer mMaxUsesPerBoot; + private final boolean mRandomizedEncryptionRequired; - private final Set<Integer> mUserAuthenticators; + private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; - private final Integer mUserAuthenticationValidityDurationSeconds; + private final int mUserAuthenticationValidityDurationSeconds; /** * Parameter specification for the "{@code AndroidKeyPairGenerator}" @@ -135,34 +137,41 @@ 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) { + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] digests, + String[] encryptionPaddings, + String[] signaturePaddings, + String[] blockModes, + boolean randomizedEncryptionRequired, + @KeyStoreKeyProperties.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 (subjectDN == null) { - throw new IllegalArgumentException("subjectDN == null"); - } else if (serialNumber == null) { - throw new IllegalArgumentException("serialNumber == null"); - } else if (startDate == null) { - throw new IllegalArgumentException("startDate == null"); - } else if (endDate == null) { - 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"); } + if (subjectDN == null) { + subjectDN = DEFAULT_CERT_SUBJECT; + } + if (startDate == null) { + startDate = DEFAULT_CERT_NOT_BEFORE; + } + if (endDate == null) { + endDate = DEFAULT_CERT_NOT_AFTER; + } + if (serialNumber == null) { + serialNumber = DEFAULT_CERT_SERIAL_NUMBER; + } + + if (endDate.before(startDate)) { + throw new IllegalArgumentException("endDate < startDate"); + } + mContext = context; mKeystoreAlias = keyStoreAlias; mKeyType = keyType; @@ -177,14 +186,13 @@ 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 = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(digests)); + mEncryptionPaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); + mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings)); + mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); + mRandomizedEncryptionRequired = randomizedEncryptionRequired; + mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -195,9 +203,28 @@ 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, // purposes + null, // digests + null, // encryption paddings + null, // signature paddings + null, // block modes + false, // randomized encryption required + 0, // user authenticators + -1 // user authentication validity duration (seconds) + ); } /** @@ -323,90 +350,76 @@ 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 @KeyStoreKeyProperties.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. + * Gets the set of digest algorithms with which the key can be used. * * @hide */ - public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() { - return mDigest; + public String[] getDigests() { + return ArrayUtils.cloneIfNotEmpty(mDigests); } /** - * 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 with which the key can be used when encrypting/decrypting. * * @hide */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; + public String[] getEncryptionPaddings() { + return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** - * 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 with which the key can be used when signing/verifying. * * @hide */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; + public String[] getSignaturePaddings() { + return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } /** - * 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 with which the key can be used. * * @hide */ - public Integer getMinSecondsBetweenOperations() { - return mMinSecondsBetweenOperations; + public String[] getBlockModes() { + return ArrayUtils.cloneIfNotEmpty(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 @KeyStoreKeyProperties.UserAuthenticatorEnum int getUserAuthenticators() { + return mUserAuthenticators; } /** @@ -416,12 +429,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 +486,21 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private Date mKeyValidityForConsumptionEnd; - private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes; + private @KeyStoreKeyProperties.PurposeEnum int mPurposes; - private @KeyStoreKeyConstraints.DigestEnum Integer mDigest; + private String[] mDigests; - private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding; + private String[] mEncryptionPaddings; - private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode; + private String[] mSignaturePaddings; - private Integer mMinSecondsBetweenOperations; + private String[] mBlockModes; - private Integer mMaxUsesPerBoot; + private boolean mRandomizedEncryptionRequired = true; - private Set<Integer> mUserAuthenticators; + private @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; - private Integer mUserAuthenticationValidityDurationSeconds; + private int mUserAuthenticationValidityDurationSeconds = -1; /** * Creates a new instance of the {@code Builder} with the given @@ -558,6 +571,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the subject used for the self-signed certificate of the * generated key pair. + * + * <p>The subject must be specified on API Level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On + * newer platforms the subject defaults to {@code CN=fake} if not specified. */ public Builder setSubject(X500Principal subject) { if (subject == null) { @@ -570,6 +587,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the serial number used for the self-signed certificate of the * generated key pair. + * + * <p>The serial number must be specified on API Level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On + * newer platforms the serial number defaults to {@code 1} if not specified. */ public Builder setSerialNumber(BigInteger serialNumber) { if (serialNumber == null) { @@ -582,6 +603,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the start of the validity period for the self-signed certificate * of the generated key pair. + * + * <p>The date must be specified on API Level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On + * newer platforms the date defaults to {@code Jan 1 1970} if not specified. */ public Builder setStartDate(Date startDate) { if (startDate == null) { @@ -594,6 +619,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the end of the validity period for the self-signed certificate * of the generated key pair. + * + * <p>The date must be specified on API Level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On + * newer platforms the date defaults to {@code Jan 1 2048} if not specified. */ public Builder setEndDate(Date endDate) { if (endDate == null) { @@ -617,7 +646,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant before which the key is not yet valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityEnd(Date) * @@ -631,7 +660,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant after which the key is no longer valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityStart(Date) * @see #setKeyValidityForConsumptionEnd(Date) @@ -648,7 +677,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant after which the key is no longer valid for encryption and signing. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForConsumptionEnd(Date) * @@ -663,7 +692,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Sets the time instant after which the key is no longer valid for decryption and * verification. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForOriginationEnd(Date) * @@ -675,84 +704,95 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** - * Restricts the purposes for which the key can be used to the provided set of purposes. + * Sets the set of purposes for which the key can be used. * - * <p>By default, the key can be used for encryption, decryption, signing, and verification. + * <p>This must be specified for all keys. There is no default. * * @hide */ - public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) { + public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; return this; } /** - * Restricts the key to being used only with the provided digest. Attempts to use the key - * with any other digests be rejected. + * Sets the set of digests with which the key can be used when signing/verifying. Attempts + * to use the key with any other digest will be rejected. * - * <p>This restriction must be specified for keys which are used for signing/verification. + * <p>This must be specified for keys which are used for signing/verification. * * @hide */ - public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) { - mDigest = digest; + public Builder setDigests(String... digests) { + mDigests = ArrayUtils.cloneIfNotEmpty(digests); return this; } /** - * Restricts the key to being used only with the provided padding scheme. Attempts to use - * the key with any other padding will be rejected. + * Sets the set of padding schemes with which the key can be used when + * encrypting/decrypting. Attempts to use the key with any other padding scheme will be + * rejected. * - * <p>This restriction must be specified for keys which are used for encryption/decryption. + * <p>This must be specified for keys which are used for encryption/decryption. * * @hide */ - public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) { - mPadding = padding; + public Builder setEncryptionPaddings(String... paddings) { + mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(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. + * Sets the set of padding schemes with which the key can be used when + * signing/verifying. Attempts to use the key with any other padding scheme will be + * rejected. * - * <p>This restriction must be specified for keys which are used for encryption/decryption. + * <p>This must be specified for RSA keys which are used for signing/verification. * * @hide */ - public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) { - mBlockMode = blockMode; + public Builder setSignaturePaddings(String... paddings) { + mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings); 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. + * Sets the set of block modes with which the key can be used when encrypting/decrypting. + * 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 applies only to private key operations. Public key operations are not - * restricted. + * <p>This must be specified for encryption/decryption keys. * * @hide */ - public Builder setMinSecondsBetweenOperations(int seconds) { - mMinSecondsBetweenOperations = seconds; + public Builder setBlockModes(String... blockModes) { + mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; } /** - * Sets the maximum number of times a key can be used without rebooting the device. + * 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, the key can be used for an unlimited number of times. + * <p>By default, {@code IND-CPA} is required. * - * <p>This restriction applies only to private key operations. Public key operations are not - * restricted. + * <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>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 +805,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( + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators) { + mUserAuthenticators = userAuthenticators; return this; } @@ -790,7 +830,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 +860,11 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, - mDigest, - mPadding, - mBlockMode, - mMinSecondsBetweenOperations, - mMaxUsesPerBoot, + mDigests, + mEncryptionPaddings, + mSignaturePaddings, + mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); } diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java index ec358d6..7bc6378 100644 --- a/keystore/java/android/security/KeyStoreCipherSpi.java +++ b/keystore/java/android/security/KeyStoreCipherSpi.java @@ -48,70 +48,71 @@ import javax.crypto.spec.IvParameterSpec; public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCryptoOperation { public abstract static class AES extends KeyStoreCipherSpi { - protected AES(@KeyStoreKeyConstraints.BlockModeEnum int blockMode, - @KeyStoreKeyConstraints.PaddingEnum int padding, boolean ivUsed) { - super(KeyStoreKeyConstraints.Algorithm.AES, - blockMode, - padding, + protected AES(int keymasterBlockMode, int keymasterPadding, boolean ivUsed) { + super(KeymasterDefs.KM_ALGORITHM_AES, + keymasterBlockMode, + keymasterPadding, 16, ivUsed); } public abstract static class ECB extends AES { - protected ECB(@KeyStoreKeyConstraints.PaddingEnum int padding) { - super(KeyStoreKeyConstraints.BlockMode.ECB, padding, false); + protected ECB(int keymasterPadding) { + super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false); } public static class NoPadding extends ECB { public NoPadding() { - super(KeyStoreKeyConstraints.Padding.NONE); + super(KeymasterDefs.KM_PAD_NONE); } } public static class PKCS7Padding extends ECB { public PKCS7Padding() { - super(KeyStoreKeyConstraints.Padding.PKCS7); + super(KeymasterDefs.KM_PAD_PKCS7); } } } public abstract static class CBC extends AES { - protected CBC(@KeyStoreKeyConstraints.BlockModeEnum int padding) { - super(KeyStoreKeyConstraints.BlockMode.CBC, padding, true); + protected CBC(int keymasterPadding) { + super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true); } public static class NoPadding extends CBC { public NoPadding() { - super(KeyStoreKeyConstraints.Padding.NONE); + super(KeymasterDefs.KM_PAD_NONE); } } public static class PKCS7Padding extends CBC { public PKCS7Padding() { - super(KeyStoreKeyConstraints.Padding.PKCS7); + super(KeymasterDefs.KM_PAD_PKCS7); } } } public abstract static class CTR extends AES { - protected CTR(@KeyStoreKeyConstraints.BlockModeEnum int padding) { - super(KeyStoreKeyConstraints.BlockMode.CTR, padding, true); + protected CTR(int keymasterPadding) { + super(KeymasterDefs.KM_MODE_CTR, keymasterPadding, true); } public static class NoPadding extends CTR { public NoPadding() { - super(KeyStoreKeyConstraints.Padding.NONE); + super(KeymasterDefs.KM_PAD_NONE); } } } } private final KeyStore mKeyStore; - private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm; - private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockMode; - private final @KeyStoreKeyConstraints.PaddingEnum int mPadding; + private final int mKeymasterAlgorithm; + private final int mKeymasterBlockMode; + private final int mKeymasterPadding; 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 +120,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 @@ -133,17 +137,17 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer; protected KeyStoreCipherSpi( - @KeyStoreKeyConstraints.AlgorithmEnum int algorithm, - @KeyStoreKeyConstraints.BlockModeEnum int blockMode, - @KeyStoreKeyConstraints.PaddingEnum int padding, + int keymasterAlgorithm, + int keymasterBlockMode, + int keymasterPadding, int blockSizeBytes, boolean ivUsed) { mKeyStore = KeyStore.getInstance(); - mAlgorithm = algorithm; - mBlockMode = blockMode; - mPadding = padding; + mKeymasterAlgorithm = keymasterAlgorithm; + mKeymasterBlockMode = keymasterBlockMode; + mKeymasterPadding = keymasterPadding; mBlockSizeBytes = blockSizeBytes; - mIvUsed = ivUsed; + mIvRequired = ivUsed; } @Override @@ -170,7 +174,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 +191,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,11 +227,17 @@ 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); - keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mBlockMode); - keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mPadding); + keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); + keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode); + keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding); addAlgorithmSpecificParametersToBegin(keymasterInputArgs); KeymasterArguments keymasterOutputArgs = new KeymasterArguments(); @@ -234,6 +262,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 +327,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry } } - reset(); + resetWhilePreservingInitState(); return output; } @@ -376,7 +405,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 +437,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 +476,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 +521,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 +544,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 +563,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 +586,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/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java index 1f8b7e4..aafd2fa 100644 --- a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java +++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java @@ -19,6 +19,8 @@ package android.security; import android.os.IBinder; import android.security.keymaster.OperationResult; +import libcore.util.EmptyArray; + import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -95,7 +97,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { // Too much input for one chunk -- extract one max-sized chunk and feed it into the // update operation. inputBytesInChunk = mMaxChunkSize - mBufferedLength; - chunk = concat(mBuffered, mBufferedOffset, mBufferedLength, + chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength, input, inputOffset, inputBytesInChunk); } else { // All of available input fits into one chunk. @@ -108,7 +110,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { } else { // Need to combine buffered data with input data into one array. inputBytesInChunk = inputLength; - chunk = concat(mBuffered, mBufferedOffset, mBufferedLength, + chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength, input, inputOffset, inputBytesInChunk); } } @@ -197,7 +199,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { // Flush all buffered input and provided input into keystore/keymaster. byte[] output = update(input, inputOffset, inputLength); - output = concat(output, flush()); + output = ArrayUtils.concat(output, flush()); OperationResult opResult = mKeyStoreStream.finish(); if (opResult == null) { @@ -206,7 +208,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { throw KeyStore.getKeyStoreException(opResult.resultCode); } - return concat(output, opResult.output); + return ArrayUtils.concat(output, opResult.output); } /** @@ -215,11 +217,11 @@ public class KeyStoreCryptoOperationChunkedStreamer { */ public byte[] flush() throws KeyStoreException { if (mBufferedLength <= 0) { - return EMPTY_BYTE_ARRAY; + return EmptyArray.BYTE; } - byte[] chunk = subarray(mBuffered, mBufferedOffset, mBufferedLength); - mBuffered = EMPTY_BYTE_ARRAY; + byte[] chunk = ArrayUtils.subarray(mBuffered, mBufferedOffset, mBufferedLength); + mBuffered = EmptyArray.BYTE; mBufferedLength = 0; mBufferedOffset = 0; @@ -238,46 +240,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { + " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed); } - return (opResult.output != null) ? opResult.output : EMPTY_BYTE_ARRAY; - } - - private static byte[] concat(byte[] arr1, byte[] arr2) { - if ((arr1 == null) || (arr1.length == 0)) { - return arr2; - } else if ((arr2 == null) || (arr2.length == 0)) { - return arr1; - } else { - byte[] result = new byte[arr1.length + arr2.length]; - System.arraycopy(arr1, 0, result, 0, arr1.length); - System.arraycopy(arr2, 0, result, arr1.length, arr2.length); - return result; - } - } - - private static byte[] concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2, - int len2) { - if (len1 == 0) { - return subarray(arr2, offset2, len2); - } else if (len2 == 0) { - return subarray(arr1, offset1, len1); - } else { - byte[] result = new byte[len1 + len2]; - System.arraycopy(arr1, offset1, result, 0, len1); - System.arraycopy(arr2, offset2, result, len1, len2); - return result; - } - } - - private static byte[] subarray(byte[] arr, int offset, int len) { - if (len == 0) { - return EMPTY_BYTE_ARRAY; - } - if ((offset == 0) && (len == arr.length)) { - return arr; - } - byte[] result = new byte[len]; - System.arraycopy(arr, offset, result, 0, len); - return result; + return (opResult.output != null) ? opResult.output : EmptyArray.BYTE; } /** diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java index a5864a4..a19bbda 100644 --- a/keystore/java/android/security/KeyStoreHmacSpi.java +++ b/keystore/java/android/security/KeyStoreHmacSpi.java @@ -35,14 +35,38 @@ import javax.crypto.MacSpi; */ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation { + public static class HmacSHA1 extends KeyStoreHmacSpi { + public HmacSHA1() { + super(KeymasterDefs.KM_DIGEST_SHA1); + } + } + + public static class HmacSHA224 extends KeyStoreHmacSpi { + public HmacSHA224() { + super(KeymasterDefs.KM_DIGEST_SHA_2_224); + } + } + public static class HmacSHA256 extends KeyStoreHmacSpi { public HmacSHA256() { - super(KeyStoreKeyConstraints.Digest.SHA256, 256 / 8); + super(KeymasterDefs.KM_DIGEST_SHA_2_256); + } + } + + public static class HmacSHA384 extends KeyStoreHmacSpi { + public HmacSHA384() { + super(KeymasterDefs.KM_DIGEST_SHA_2_384); + } + } + + public static class HmacSHA512 extends KeyStoreHmacSpi { + public HmacSHA512() { + super(KeymasterDefs.KM_DIGEST_SHA_2_512); } } private final KeyStore mKeyStore = KeyStore.getInstance(); - private final @KeyStoreKeyConstraints.DigestEnum int mDigest; + private final int mKeymasterDigest; private final int mMacSizeBytes; private String mKeyAliasInKeyStore; @@ -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) { - mDigest = digest; - mMacSizeBytes = macSizeBytes; + protected KeyStoreHmacSpi(int keymasterDigest) { + mKeymasterDigest = keymasterDigest; + mMacSizeBytes = KeymasterUtils.getDigestOutputSizeBytes(keymasterDigest); } @Override @@ -105,8 +129,8 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp } KeymasterArguments keymasterArgs = new KeymasterArguments(); - keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeyStoreKeyConstraints.Algorithm.HMAC); - keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mDigest); + keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC); + keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); OperationResult opResult = mKeyStore.begin(mKeyAliasInKeyStore, KeymasterDefs.KM_PURPOSE_SIGN, diff --git a/keystore/java/android/security/KeyStoreKeyCharacteristics.java b/keystore/java/android/security/KeyStoreKeyCharacteristics.java deleted file mode 100644 index 543b5d8..0000000 --- a/keystore/java/android/security/KeyStoreKeyCharacteristics.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -package android.security; - -import android.annotation.IntDef; -import android.security.keymaster.KeymasterDefs; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Characteristics of {@code AndroidKeyStore} keys. - * - * @hide - */ -public abstract class KeyStoreKeyCharacteristics { - private KeyStoreKeyCharacteristics() {} - - @Retention(RetentionPolicy.SOURCE) - @IntDef({Origin.GENERATED_INSIDE_TEE, Origin.GENERATED_OUTSIDE_OF_TEE, Origin.IMPORTED}) - public @interface OriginEnum {} - - /** - * Origin of the key. - */ - public static abstract class Origin { - private Origin() {} - - /** Key was generated inside a TEE. */ - public static final int GENERATED_INSIDE_TEE = 1; - - /** Key was generated outside of a TEE. */ - public static final int GENERATED_OUTSIDE_OF_TEE = 2; - - /** Key was imported. */ - public static final int IMPORTED = 0; - - /** - * @hide - */ - public static @OriginEnum int fromKeymaster(int origin) { - switch (origin) { - case KeymasterDefs.KM_ORIGIN_HARDWARE: - return GENERATED_INSIDE_TEE; - case KeymasterDefs.KM_ORIGIN_SOFTWARE: - return GENERATED_OUTSIDE_OF_TEE; - case KeymasterDefs.KM_ORIGIN_IMPORTED: - return IMPORTED; - default: - throw new IllegalArgumentException("Unknown origin: " + origin); - } - } - } -} diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java deleted file mode 100644 index c27ccb1..0000000 --- a/keystore/java/android/security/KeyStoreKeyConstraints.java +++ /dev/null @@ -1,609 +0,0 @@ -/* - * 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. - */ - -package android.security; - -import android.annotation.IntDef; -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. - * - * @hide - */ -public abstract class KeyStoreKeyConstraints { - private KeyStoreKeyConstraints() {} - - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag=true, value={Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY}) - public @interface PurposeEnum {} - - /** - * Purpose of key. - */ - public static abstract class Purpose { - private Purpose() {} - - /** - * Purpose: encryption. - */ - public static final int ENCRYPT = 1 << 0; - - /** - * Purpose: decryption. - */ - public static final int DECRYPT = 1 << 1; - - /** - * Purpose: signing. - */ - public static final int SIGN = 1 << 2; - - /** - * Purpose: signature verification. - */ - 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) { - switch (purpose) { - case ENCRYPT: - return KeymasterDefs.KM_PURPOSE_ENCRYPT; - case DECRYPT: - return KeymasterDefs.KM_PURPOSE_DECRYPT; - case SIGN: - return KeymasterDefs.KM_PURPOSE_SIGN; - case VERIFY: - return KeymasterDefs.KM_PURPOSE_VERIFY; - default: - throw new IllegalArgumentException("Unknown purpose: " + purpose); - } - } - - /** - * @hide - */ - public static @PurposeEnum int fromKeymaster(int purpose) { - switch (purpose) { - case KeymasterDefs.KM_PURPOSE_ENCRYPT: - return ENCRYPT; - case KeymasterDefs.KM_PURPOSE_DECRYPT: - return DECRYPT; - case KeymasterDefs.KM_PURPOSE_SIGN: - return SIGN; - case KeymasterDefs.KM_PURPOSE_VERIFY: - return VERIFY; - default: - throw new IllegalArgumentException("Unknown purpose: " + purpose); - } - } - - /** - * @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; - } - } - return Arrays.copyOf(result, resultCount); - } - - /** - * @hide - */ - public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) { - @PurposeEnum int result = 0; - for (int keymasterPurpose : purposes) { - result |= fromKeymaster(keymasterPurpose); - } - return result; - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({Algorithm.AES, Algorithm.HMAC}) - public @interface AlgorithmEnum {} - - /** - * Key algorithm. - */ - public static abstract class Algorithm { - private Algorithm() {} - - /** - * Key algorithm: AES. - */ - public static final int AES = 0; - - /** - * Key algorithm: HMAC. - */ - public static final int HMAC = 1; - - /** - * @hide - */ - public static int toKeymaster(@AlgorithmEnum int algorithm) { - switch (algorithm) { - case AES: - return KeymasterDefs.KM_ALGORITHM_AES; - case HMAC: - return KeymasterDefs.KM_ALGORITHM_HMAC; - default: - throw new IllegalArgumentException("Unknown algorithm: " + algorithm); - } - } - - /** - * @hide - */ - public static @AlgorithmEnum int fromKeymaster(int algorithm) { - switch (algorithm) { - case KeymasterDefs.KM_ALGORITHM_AES: - return AES; - case KeymasterDefs.KM_ALGORITHM_HMAC: - return HMAC; - default: - throw new IllegalArgumentException("Unknown algorithm: " + algorithm); - } - } - - /** - * @hide - */ - public static String toString(@AlgorithmEnum int algorithm) { - switch (algorithm) { - case AES: - return "AES"; - case HMAC: - return "HMAC"; - default: - throw new IllegalArgumentException("Unknown algorithm: " + algorithm); - } - } - - /** - * @hide - */ - public static @AlgorithmEnum int fromJCASecretKeyAlgorithm(String algorithm) { - if (algorithm == null) { - throw new NullPointerException("algorithm == null"); - } else if ("AES".equalsIgnoreCase(algorithm)) { - return AES; - } else if (algorithm.toLowerCase(Locale.US).startsWith("hmac")) { - return HMAC; - } else { - throw new IllegalArgumentException( - "Unsupported secret key algorithm: " + algorithm); - } - } - - /** - * @hide - */ - public static String toJCASecretKeyAlgorithm(@AlgorithmEnum int algorithm, - @DigestEnum Integer digest) { - switch (algorithm) { - case AES: - return "AES"; - case HMAC: - if (digest == null) { - throw new IllegalArgumentException("HMAC digest not specified"); - } - switch (digest) { - case Digest.SHA256: - return "HmacSHA256"; - default: - throw new IllegalArgumentException( - "Unsupported HMAC digest: " + digest); - } - default: - throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm); - } - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({Padding.NONE, Padding.ZERO, Padding.PKCS7}) - public @interface PaddingEnum {} - - /** - * Padding for signing and encryption. - */ - public static abstract class Padding { - private Padding() {} - - /** - * No padding. - */ - public static final int NONE = 0; - - /** - * Pad with zeros. - */ - public static final int ZERO = 1; - - /** - * PKCS#7 padding. - */ - public static final int PKCS7 = 2; - - /** - * @hide - */ - public static int toKeymaster(int padding) { - switch (padding) { - case NONE: - return KeymasterDefs.KM_PAD_NONE; - case ZERO: - return KeymasterDefs.KM_PAD_ZERO; - case PKCS7: - return KeymasterDefs.KM_PAD_PKCS7; - default: - throw new IllegalArgumentException("Unknown padding: " + padding); - } - } - - /** - * @hide - */ - public static @PaddingEnum int fromKeymaster(int padding) { - switch (padding) { - case KeymasterDefs.KM_PAD_NONE: - return NONE; - case KeymasterDefs.KM_PAD_ZERO: - return ZERO; - case KeymasterDefs.KM_PAD_PKCS7: - return PKCS7; - default: - throw new IllegalArgumentException("Unknown padding: " + padding); - } - } - - /** - * @hide - */ - public static String toString(@PaddingEnum int padding) { - switch (padding) { - case NONE: - return "NONE"; - case ZERO: - return "ZERO"; - case PKCS7: - return "PKCS#7"; - default: - throw new IllegalArgumentException("Unknown padding: " + padding); - } - } - - /** - * @hide - */ - public static @PaddingEnum int fromJCAPadding(String padding) { - String paddingLower = padding.toLowerCase(Locale.US); - if ("nopadding".equals(paddingLower)) { - return NONE; - } else if ("pkcs7padding".equals(paddingLower)) { - return PKCS7; - } else { - throw new IllegalArgumentException("Unknown padding: " + padding); - } - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({Digest.NONE, Digest.SHA256}) - public @interface DigestEnum {} - - /** - * Digests that can be used with a key when signing or generating Message Authentication - * Codes (MACs). - */ - public static abstract class Digest { - private Digest() {} - - /** - * No digest: sign/authenticate the raw message. - */ - public static final int NONE = 0; - - /** - * SHA-256 digest. - */ - public static final int SHA256 = 1; - - /** - * @hide - */ - public static String toString(@DigestEnum int digest) { - switch (digest) { - case NONE: - return "NONE"; - case SHA256: - return "SHA256"; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static int toKeymaster(@DigestEnum int digest) { - switch (digest) { - case NONE: - return KeymasterDefs.KM_DIGEST_NONE; - case SHA256: - return KeymasterDefs.KM_DIGEST_SHA_2_256; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static @DigestEnum int fromKeymaster(int digest) { - switch (digest) { - case KeymasterDefs.KM_DIGEST_NONE: - return NONE; - case KeymasterDefs.KM_DIGEST_SHA_2_256: - return SHA256; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) { - String algorithmLower = algorithm.toLowerCase(Locale.US); - if (algorithmLower.startsWith("hmac")) { - if ("hmacsha256".equals(algorithmLower)) { - return SHA256; - } else { - throw new IllegalArgumentException("Unsupported digest: " - + algorithmLower.substring("hmac".length())); - } - } else { - return null; - } - } - - /** - * @hide - */ - public static String toJCASignatureAlgorithmDigest(@DigestEnum int digest) { - switch (digest) { - case NONE: - return "NONE"; - case SHA256: - return "SHA256"; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static Integer getOutputSizeBytes(@DigestEnum int digest) { - switch (digest) { - case NONE: - return null; - case SHA256: - return 256 / 8; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({BlockMode.ECB, BlockMode.CBC, BlockMode.CTR}) - public @interface BlockModeEnum {} - - /** - * Block modes that can be used when encrypting/decrypting using a key. - */ - public static abstract class BlockMode { - private BlockMode() {} - - /** Electronic Codebook (ECB) block mode. */ - public static final int ECB = 0; - - /** Cipher Block Chaining (CBC) block mode. */ - public static final int CBC = 1; - - /** Counter (CTR) block mode. */ - public static final int CTR = 2; - - /** - * @hide - */ - public static int toKeymaster(@BlockModeEnum int mode) { - switch (mode) { - case ECB: - return KeymasterDefs.KM_MODE_ECB; - case CBC: - return KeymasterDefs.KM_MODE_CBC; - case CTR: - return KeymasterDefs.KM_MODE_CTR; - default: - throw new IllegalArgumentException("Unknown block mode: " + mode); - } - } - - /** - * @hide - */ - public static @BlockModeEnum int fromKeymaster(int mode) { - switch (mode) { - case KeymasterDefs.KM_MODE_ECB: - return ECB; - case KeymasterDefs.KM_MODE_CBC: - return CBC; - case KeymasterDefs.KM_MODE_CTR: - return CTR; - default: - throw new IllegalArgumentException("Unknown block mode: " + mode); - } - } - - /** - * @hide - */ - public static String toString(@BlockModeEnum int mode) { - switch (mode) { - case ECB: - return "ECB"; - case CBC: - return "CBC"; - case CTR: - return "CTR"; - default: - throw new IllegalArgumentException("Unknown block mode: " + mode); - } - } - - /** - * @hide - */ - public static @BlockModeEnum int fromJCAMode(String mode) { - String modeLower = mode.toLowerCase(Locale.US); - if ("ecb".equals(modeLower)) { - return ECB; - } else if ("cbc".equals(modeLower)) { - return CBC; - } else if ("ctr".equals(modeLower)) { - return CTR; - } else { - throw new IllegalArgumentException("Unknown block mode: " + mode); - } - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({UserAuthenticator.LOCK_SCREEN}) - public @interface UserAuthenticatorEnum {} - - /** - * User authenticators which can be used to restrict/protect access to keys. - */ - public static abstract class UserAuthenticator { - private UserAuthenticator() {} - - /** Lock screen. */ - public static final int LOCK_SCREEN = 1; - - /** - * @hide - */ - public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) { - switch (userAuthenticator) { - case LOCK_SCREEN: - return LOCK_SCREEN; - default: - throw new IllegalArgumentException( - "Unknown user authenticator: " + userAuthenticator); - } - } - - /** - * @hide - */ - public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) { - switch (userAuthenticator) { - case LOCK_SCREEN: - return LOCK_SCREEN; - default: - throw new IllegalArgumentException( - "Unknown user authenticator: " + userAuthenticator); - } - } - - /** - * @hide - */ - public static int allToKeymaster(Set<Integer> userAuthenticators) { - int result = 0; - for (@UserAuthenticatorEnum int userAuthenticator : userAuthenticators) { - result |= toKeymaster(userAuthenticator); - } - return result; - } - - /** - * @hide - */ - public static Set<Integer> allFromKeymaster(int userAuthenticators) { - 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)); - } - userAuthenticators >>>= 1; - userAuthenticator <<= 1; - } - return (result != null) ? result : Collections.<Integer>emptySet(); - } - - /** - * @hide - */ - public static String toString(@UserAuthenticatorEnum int userAuthenticator) { - switch (userAuthenticator) { - case LOCK_SCREEN: - return "LOCK_SCREEN"; - default: - throw new IllegalArgumentException( - "Unknown user authenticator: " + userAuthenticator); - } - } - } -} diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java index c9c9bd8..87e7ee6 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -37,39 +37,68 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { public static class AES extends KeyStoreKeyGeneratorSpi { public AES() { - super(KeyStoreKeyConstraints.Algorithm.AES, 128); + super(KeymasterDefs.KM_ALGORITHM_AES, 128); } } - public static class HmacSHA256 extends KeyStoreKeyGeneratorSpi { + protected static abstract class HmacBase extends KeyStoreKeyGeneratorSpi { + protected HmacBase(int keymasterDigest) { + super(KeymasterDefs.KM_ALGORITHM_HMAC, + keymasterDigest, + KeymasterUtils.getDigestOutputSizeBytes(keymasterDigest) * 8); + } + } + + public static class HmacSHA1 extends HmacBase { + public HmacSHA1() { + super(KeymasterDefs.KM_DIGEST_SHA1); + } + } + + public static class HmacSHA224 extends HmacBase { + public HmacSHA224() { + super(KeymasterDefs.KM_DIGEST_SHA_2_224); + } + } + + public static class HmacSHA256 extends HmacBase { public HmacSHA256() { - super(KeyStoreKeyConstraints.Algorithm.HMAC, - KeyStoreKeyConstraints.Digest.SHA256, - KeyStoreKeyConstraints.Digest.getOutputSizeBytes( - KeyStoreKeyConstraints.Digest.SHA256) * 8); + super(KeymasterDefs.KM_DIGEST_SHA_2_256); + } + } + + public static class HmacSHA384 extends HmacBase { + public HmacSHA384() { + super(KeymasterDefs.KM_DIGEST_SHA_2_384); + } + } + + public static class HmacSHA512 extends HmacBase { + public HmacSHA512() { + super(KeymasterDefs.KM_DIGEST_SHA_2_512); } } private final KeyStore mKeyStore = KeyStore.getInstance(); - private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm; - private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest; + private final int mKeymasterAlgorithm; + private final int mKeymasterDigest; private final int mDefaultKeySizeBits; private KeyGeneratorSpec mSpec; private SecureRandom mRng; protected KeyStoreKeyGeneratorSpi( - @KeyStoreKeyConstraints.AlgorithmEnum int algorithm, + int keymasterAlgorithm, int defaultKeySizeBits) { - this(algorithm, null, defaultKeySizeBits); + this(keymasterAlgorithm, -1, defaultKeySizeBits); } protected KeyStoreKeyGeneratorSpi( - @KeyStoreKeyConstraints.AlgorithmEnum int algorithm, - @KeyStoreKeyConstraints.DigestEnum Integer digest, + int keymasterAlgorithm, + int keymasterDigest, int defaultKeySizeBits) { - mAlgorithm = algorithm; - mDigest = digest; + mKeymasterAlgorithm = keymasterAlgorithm; + mKeymasterDigest = keymasterDigest; mDefaultKeySizeBits = defaultKeySizeBits; } @@ -88,60 +117,58 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } KeymasterArguments args = new KeymasterArguments(); - args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, - KeyStoreKeyConstraints.Algorithm.toKeymaster(mAlgorithm)); - if (mDigest != null) { - args.addInt(KeymasterDefs.KM_TAG_DIGEST, - KeyStoreKeyConstraints.Digest.toKeymaster(mDigest)); - Integer digestOutputSizeBytes = - KeyStoreKeyConstraints.Digest.getOutputSizeBytes(mDigest); - if (digestOutputSizeBytes != null) { + args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); + if (mKeymasterDigest != -1) { + args.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); + int digestOutputSizeBytes = + KeymasterUtils.getDigestOutputSizeBytes(mKeymasterDigest); + if (digestOutputSizeBytes != -1) { // 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 (mAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) { - if (mDigest == null) { - throw new IllegalStateException("Digest algorithm must be specified for key" - + " algorithm " + KeyStoreKeyConstraints.Algorithm.toString(mAlgorithm)); + if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { + if (mKeymasterDigest == -1) { + throw new IllegalStateException("Digest algorithm must be specified for HMAC key"); } } 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); + @KeyStoreKeyProperties.PurposeEnum int purposes = spec.getPurposes(); + int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes( + spec.getBlockModes()); + if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) + && (spec.isRandomizedEncryptionRequired())) { + for (int keymasterBlockMode : keymasterBlockModes) { + if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) { + throw new IllegalStateException( + "Randomized encryption (IND-CPA) required but may be violated by block" + + " mode: " + + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode( + keymasterBlockMode) + + ". See KeyGeneratorSpec documentation."); + } + } + } + for (int keymasterPurpose : - KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { + KeyStoreKeyProperties.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()); - } - if (spec.getMinSecondsBetweenOperations() != null) { - args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS, - spec.getMinSecondsBetweenOperations()); - } - if (spec.getUserAuthenticators().isEmpty()) { + args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes); + args.addInts( + KeymasterDefs.KM_TAG_PADDING, + KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings( + spec.getEncryptionPaddings())); + if (spec.getUserAuthenticators() == 0) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); } else { args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, - KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster( + KeyStoreKeyProperties.UserAuthenticator.allToKeymaster( spec.getUserAuthenticators())); } - if (spec.getUserAuthenticationValidityDurationSeconds() != null) { + if (spec.getUserAuthenticationValidityDurationSeconds() != -1) { args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, spec.getUserAuthenticationValidityDurationSeconds()); } @@ -155,9 +182,9 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { (spec.getKeyValidityForConsumptionEnd() != null) ? 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. + if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) + && (!spec.isRandomizedEncryptionRequired())) { + // Permit caller-provided IV when encrypting with this key args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); } @@ -176,7 +203,7 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { throw KeyStore.getCryptoOperationException(errorCode); } String keyAlgorithmJCA = - KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(mAlgorithm, mDigest); + KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest); return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA); } diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java new file mode 100644 index 0000000..d8ad1d3 --- /dev/null +++ b/keystore/java/android/security/KeyStoreKeyProperties.java @@ -0,0 +1,274 @@ +/* + * 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. + */ + +package android.security; + +import android.annotation.IntDef; +import android.security.keymaster.KeymasterDefs; + +import libcore.util.EmptyArray; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collection; + +/** + * Properties of {@code AndroidKeyStore} keys. + * + * @hide + */ +public abstract class KeyStoreKeyProperties { + private KeyStoreKeyProperties() {} + + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, + value = {Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY}) + public @interface PurposeEnum {} + + /** + * Purpose of key. + */ + public static abstract class Purpose { + private Purpose() {} + + /** + * Purpose: encryption. + */ + public static final int ENCRYPT = 1 << 0; + + /** + * Purpose: decryption. + */ + public static final int DECRYPT = 1 << 1; + + /** + * Purpose: signing. + */ + public static final int SIGN = 1 << 2; + + /** + * Purpose: signature verification. + */ + public static final int VERIFY = 1 << 3; + + /** + * @hide + */ + public static int toKeymaster(@PurposeEnum int purpose) { + switch (purpose) { + case ENCRYPT: + return KeymasterDefs.KM_PURPOSE_ENCRYPT; + case DECRYPT: + return KeymasterDefs.KM_PURPOSE_DECRYPT; + case SIGN: + return KeymasterDefs.KM_PURPOSE_SIGN; + case VERIFY: + return KeymasterDefs.KM_PURPOSE_VERIFY; + default: + throw new IllegalArgumentException("Unknown purpose: " + purpose); + } + } + + /** + * @hide + */ + public static @PurposeEnum int fromKeymaster(int purpose) { + switch (purpose) { + case KeymasterDefs.KM_PURPOSE_ENCRYPT: + return ENCRYPT; + case KeymasterDefs.KM_PURPOSE_DECRYPT: + return DECRYPT; + case KeymasterDefs.KM_PURPOSE_SIGN: + return SIGN; + case KeymasterDefs.KM_PURPOSE_VERIFY: + return VERIFY; + default: + throw new IllegalArgumentException("Unknown purpose: " + purpose); + } + } + + /** + * @hide + */ + 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 result; + } + + /** + * @hide + */ + public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) { + @PurposeEnum int result = 0; + for (int keymasterPurpose : purposes) { + result |= fromKeymaster(keymasterPurpose); + } + return result; + } + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, + value = {UserAuthenticator.LOCK_SCREEN}) + public @interface UserAuthenticatorEnum {} + + /** + * User authenticators which can be used to restrict/protect access to keys. + */ + public static abstract class UserAuthenticator { + private UserAuthenticator() {} + + /** Lock screen. */ + public static final int LOCK_SCREEN = 1 << 0; + + /** + * @hide + */ + public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) { + switch (userAuthenticator) { + case LOCK_SCREEN: + return KeymasterDefs.HW_AUTH_PASSWORD; + default: + throw new IllegalArgumentException( + "Unknown user authenticator: " + userAuthenticator); + } + } + + /** + * @hide + */ + public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) { + switch (userAuthenticator) { + case KeymasterDefs.HW_AUTH_PASSWORD: + return LOCK_SCREEN; + default: + throw new IllegalArgumentException( + "Unknown user authenticator: " + userAuthenticator); + } + } + + /** + * @hide + */ + public static int allToKeymaster(@UserAuthenticatorEnum int userAuthenticators) { + int result = 0; + int userAuthenticator = 1; + while (userAuthenticators != 0) { + if ((userAuthenticators & 1) != 0) { + result |= toKeymaster(userAuthenticator); + } + userAuthenticators >>>= 1; + userAuthenticator <<= 1; + } + return result; + } + + /** + * @hide + */ + public static @UserAuthenticatorEnum int allFromKeymaster(int userAuthenticators) { + @UserAuthenticatorEnum int result = 0; + int userAuthenticator = 1; + while (userAuthenticators != 0) { + if ((userAuthenticators & 1) != 0) { + result |= fromKeymaster(userAuthenticator); + } + userAuthenticators >>>= 1; + userAuthenticator <<= 1; + } + return result; + } + + /** + * @hide + */ + public static String toString(@UserAuthenticatorEnum int userAuthenticator) { + switch (userAuthenticator) { + case LOCK_SCREEN: + return "LOCK_SCREEN"; + default: + throw new IllegalArgumentException( + "Unknown user authenticator: " + userAuthenticator); + } + } + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({Origin.GENERATED, Origin.IMPORTED}) + public @interface OriginEnum {} + + /** + * Origin of the key. + */ + public static abstract class Origin { + private Origin() {} + + /** Key was generated inside AndroidKeyStore. */ + public static final int GENERATED = 1 << 0; + + /** Key was imported into AndroidKeyStore. */ + public static final int IMPORTED = 1 << 1; + + /** + * @hide + */ + public static @OriginEnum int fromKeymaster(int origin) { + switch (origin) { + case KeymasterDefs.KM_ORIGIN_HARDWARE: + return GENERATED; + case KeymasterDefs.KM_ORIGIN_IMPORTED: + return IMPORTED; + default: + throw new IllegalArgumentException("Unknown origin: " + origin); + } + } + } + + private static int[] getSetFlags(int flags) { + if (flags == 0) { + return EmptyArray.INT; + } + 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/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java index ddeefbd..861ed34 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 @@ -31,58 +28,55 @@ import java.util.Set; public class KeyStoreKeySpec implements KeySpec { private final String mKeystoreAlias; private final int mKeySize; - private final @KeyStoreKeyCharacteristics.OriginEnum int mOrigin; + private final boolean mTeeBacked; + private final @KeyStoreKeyProperties.OriginEnum int mOrigin; private final Date mKeyValidityStart; private final Date mKeyValidityForOriginationEnd; 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 @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private final String[] mEncryptionPaddings; + private final String[] mSignaturePaddings; + private final String[] mDigests; + private final String[] mBlockModes; + private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; + private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mTeeEnforcedUserAuthenticators; + private final int mUserAuthenticationValidityDurationSeconds; /** * @hide */ KeyStoreKeySpec(String keystoreKeyAlias, - @KeyStoreKeyCharacteristics.OriginEnum int origin, - int keySize, Date keyValidityStart, Date keyValidityForOriginationEnd, + boolean teeBacked, + @KeyStoreKeyProperties.OriginEnum int origin, + 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) { + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] encryptionPaddings, + String[] signaturePaddings, + String[] digests, + String[] blockModes, + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators, + @KeyStoreKeyProperties.UserAuthenticatorEnum int teeEnforcedUserAuthenticators, + int userAuthenticationValidityDurationSeconds) { mKeystoreAlias = keystoreKeyAlias; + mTeeBacked = teeBacked; mOrigin = origin; mKeySize = keySize; mKeyValidityStart = keyValidityStart; 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(); - mTeeBackedUserAuthenticators = (teeBackedUserAuthenticators != null) - ? new HashSet<Integer>(teeBackedUserAuthenticators) - : Collections.<Integer>emptySet(); + mEncryptionPaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); + mSignaturePaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings)); + mDigests = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(digests)); + mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); + mUserAuthenticators = userAuthenticators; + mTeeEnforcedUserAuthenticators = teeEnforcedUserAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -94,14 +88,22 @@ public class KeyStoreKeySpec implements KeySpec { } /** + * Returns {@code true} if the key is TEE-backed. Key material of TEE-backed keys is available + * in plaintext only inside the TEE. + */ + public boolean isTeeBacked() { + return mTeeBacked; + } + + /** * Gets the origin of the key. */ - public @KeyStoreKeyCharacteristics.OriginEnum int getOrigin() { + public @KeyStoreKeyProperties.OriginEnum int getOrigin() { return mOrigin; } /** - * Gets the key's size in bits. + * Gets the size of the key in bits. */ public int getKeySize() { return mKeySize; @@ -137,90 +139,65 @@ public class KeyStoreKeySpec implements KeySpec { /** * Gets the set of purposes for which the key can be used. */ - public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() { + public @KeyStoreKeyProperties.PurposeEnum int getPurposes() { return mPurposes; } /** - * Gets the algorithm of the key. + * Gets the set of block modes with which the key can be used. */ - public @KeyStoreKeyConstraints.AlgorithmEnum int getAlgorithm() { - return mAlgorithm; + public String[] getBlockModes() { + return ArrayUtils.cloneIfNotEmpty(mBlockModes); } /** - * 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 padding modes with which the key can be used when encrypting/decrypting. */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; + public String[] getEncryptionPaddings() { + return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** - * 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. + * Gets the set of padding modes with which the key can be used when signing/verifying. */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; + public String[] getSignaturePaddings() { + return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } /** - * 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. - */ - public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() { - return mDigest; - } - - /** - * 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 String[] getDigests() { + return ArrayUtils.cloneIfNotEmpty(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 @KeyStoreKeyProperties.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 @KeyStoreKeyProperties.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..9fce177 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -18,16 +18,15 @@ package android.security; import android.content.Context; -import java.security.KeyPairGenerator; +import java.security.Key; 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 - * {@code KeyStore} entries that work with + * Parameters specifying how to secure and restrict the use of a key being + * imported into the * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore * facility</a>. The Android KeyStore facility is accessed through a * {@link java.security.KeyStore} API using the {@code AndroidKeyStore} @@ -38,43 +37,35 @@ import java.util.Set; * there is only one logical instance of the {@code KeyStore} per application * UID so apps using the {@code sharedUid} facility will also share a * {@code KeyStore}. - * <p> - * Keys may be generated using the {@link KeyPairGenerator} facility with a - * {@link KeyPairGeneratorSpec} to specify the entry's {@code alias}. A - * self-signed X.509 certificate will be attached to generated entries, but that - * may be replaced at a later time by a certificate signed by a real Certificate - * Authority. */ public final class KeyStoreParameter implements ProtectionParameter { private int mFlags; 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 @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private final String[] mEncryptionPaddings; + private final String[] mSignaturePaddings; + private final String[] mDigests; + private final String[] mBlockModes; + private final boolean mRandomizedEncryptionRequired; + private final @KeyStoreKeyProperties.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)) { + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] encryptionPaddings, + String[] signaturePaddings, + String[] digests, + String[] blockModes, + boolean randomizedEncryptionRequired, + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators, + int userAuthenticationValidityDurationSeconds) { + if ((userAuthenticationValidityDurationSeconds < 0) + && (userAuthenticationValidityDurationSeconds != -1)) { throw new IllegalArgumentException( "userAuthenticationValidityDurationSeconds must not be negative"); } @@ -84,15 +75,14 @@ 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(); + mEncryptionPaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); + mSignaturePaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings)); + mDigests = ArrayUtils.cloneIfNotEmpty(digests); + mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); + mRandomizedEncryptionRequired = randomizedEncryptionRequired; + mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -144,105 +134,107 @@ 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 @KeyStoreKeyProperties.PurposeEnum int getPurposes() { return mPurposes; } /** - * Gets the algorithm to which the key is restricted. + * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. * - * @return algorithm or {@code null} if it's not restricted. * @hide */ - public @KeyStoreKeyConstraints.AlgorithmEnum Integer getAlgorithm() { - return mAlgorithm; + public String[] getEncryptionPaddings() { + return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** - * 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 with which the key can be used when signing or verifying + * signatures. * * @hide */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; + public String[] getSignaturePaddings() { + return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } /** - * Gets the digest to which the key is restricted when generating signatures or Message - * Authentication Codes (MACs). + * Gets the set of digest algorithms with which the key can be used. * - * @return digest or {@code null} if the digest is not restricted. + * @throws IllegalStateException if this set has not been specified. + * + * @see #isDigestsSpecified() * * @hide */ - public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() { - return mDigest; + public String[] getDigests() { + if (mDigests == null) { + throw new IllegalStateException("Digests not specified"); + } + return ArrayUtils.cloneIfNotEmpty(mDigests); } /** - * Gets the block mode to which the key is restricted when used for encryption or decryption. + * Returns {@code true} if the set of digest algorithms with which the key can be used has 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 with which the key can be used. * * @hide */ - public Integer getMinSecondsBetweenOperations() { - return mMinSecondsBetweenOperations; + public String[] getBlockModes() { + return ArrayUtils.cloneIfNotEmpty(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 @KeyStoreKeyProperties.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 +260,14 @@ 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 @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private String[] mEncryptionPaddings; + private String[] mSignaturePaddings; + private String[] mDigests; + private String[] mBlockModes; + private boolean mRandomizedEncryptionRequired = true; + private @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; + private int mUserAuthenticationValidityDurationSeconds = -1; /** * Creates a new instance of the {@code Builder} with the given @@ -310,7 +301,7 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Sets the time instant before which the key is not yet valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityEnd(Date) * @@ -324,7 +315,7 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Sets the time instant after which the key is no longer valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityStart(Date) * @see #setKeyValidityForConsumptionEnd(Date) @@ -341,7 +332,7 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Sets the time instant after which the key is no longer valid for encryption and signing. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForConsumptionEnd(Date) * @@ -356,7 +347,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * Sets the time instant after which the key is no longer valid for decryption and * verification. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForOriginationEnd(Date) * @@ -368,96 +359,111 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * Restricts the purposes for which the key can be used to the provided set of purposes. + * Sets the set of purposes for which the key can be used. * - * <p>By default, the key can be used for encryption, decryption, signing, and verification. + * <p>This must be specified for all keys. There is no default. * * @hide */ - public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) { + public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; return this; } /** - * Sets the algorithm of the key. + * Sets the set of padding schemes with which the key can be used when + * encrypting/decrypting. Attempts to use the key with any other padding scheme will be + * rejected. * - * <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. + * <p>This must be specified for keys which are used for encryption/decryption. * * @hide */ - public Builder setAlgorithm(@KeyStoreKeyConstraints.AlgorithmEnum int algorithm) { - mAlgorithm = algorithm; + public Builder setEncryptionPaddings(String... paddings) { + mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); return this; } /** - * Restricts the key to being used only with the provided padding scheme. Attempts to use - * the key with any other padding will be rejected. + * Sets the set of padding schemes with which the key can be used when + * signing/verifying. Attempts to use the key with any other padding scheme will be + * rejected. * - * <p>This restriction must be specified for keys which are used for encryption/decryption. + * <p>This must be specified for RSA keys which are used for signing/verification. * * @hide */ - public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) { - mPadding = padding; + public Builder setSignaturePaddings(String... paddings) { + mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings); 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; - 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. + * Sets the set of digests with which the key can be used when signing/verifying or + * generating MACs. 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 the digest specified in {@link Key#getAlgorithm()}. For + * asymmetric signing keys this constraint must be specified. * * @hide */ - public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) { - mBlockMode = blockMode; + public Builder setDigests(String... digests) { + mDigests = ArrayUtils.cloneIfNotEmpty(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. + * Sets the set of block modes with which the key can be used when encrypting/decrypting. + * 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 must be specified for encryption/decryption keys. * * @hide */ - public Builder setMinSecondsBetweenOperations(int seconds) { - mMinSecondsBetweenOperations = seconds; + public Builder setBlockModes(String... blockModes) { + mBlockModes = ArrayUtils.cloneIfNotEmpty(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 +473,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( + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators) { + mUserAuthenticators = userAuthenticators; return this; } @@ -489,7 +495,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 +516,11 @@ public final class KeyStoreParameter implements ProtectionParameter { mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, - mAlgorithm, - mPadding, - mDigest, - mBlockMode, - mMinSecondsBetweenOperations, - mMaxUsesPerBoot, + mEncryptionPaddings, + mSignaturePaddings, + mDigests, + mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); } diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java index f552759..33073a4 100644 --- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java @@ -19,11 +19,14 @@ package android.security; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterDefs; +import libcore.util.EmptyArray; + import java.security.InvalidKeyException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; +import java.util.ArrayList; import java.util.Date; -import java.util.Set; +import java.util.List; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactorySpi; @@ -71,94 +74,107 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { + " Keystore error: " + errorCode); } - @KeyStoreKeyCharacteristics.OriginEnum Integer origin; + boolean teeBacked; + @KeyStoreKeyProperties.OriginEnum int origin; int keySize; - @KeyStoreKeyConstraints.PurposeEnum int purposes; - @KeyStoreKeyConstraints.AlgorithmEnum int algorithm; - @KeyStoreKeyConstraints.PaddingEnum Integer padding; - @KeyStoreKeyConstraints.DigestEnum Integer digest; - @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode; + @KeyStoreKeyProperties.PurposeEnum int purposes; + String[] encryptionPaddings; + String[] digests; + String[] blockModes; + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators; + @KeyStoreKeyProperties.UserAuthenticatorEnum int teeEnforcedUserAuthenticators; try { - origin = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ORIGIN); - if (origin == null) { + if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { + teeBacked = true; + origin = KeyStoreKeyProperties.Origin.fromKeymaster( + keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1)); + } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { + teeBacked = false; + origin = KeyStoreKeyProperties.Origin.fromKeymaster( + keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1)); + } else { throw new InvalidKeySpecException("Key origin not available"); } - origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster(origin); - Integer keySizeInteger = - KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_KEY_SIZE); + Integer keySizeInteger = keyCharacteristics.getInteger(KeymasterDefs.KM_TAG_KEY_SIZE); if (keySizeInteger == null) { throw new InvalidKeySpecException("Key size not available"); } keySize = keySizeInteger; - purposes = KeyStoreKeyConstraints.Purpose.allFromKeymaster( - KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_PURPOSE)); - Integer alg = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ALGORITHM); - if (alg == null) { - 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); + purposes = KeyStoreKeyProperties.Purpose.allFromKeymaster( + keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PURPOSE)); + + List<String> encryptionPaddingsList = new ArrayList<String>(); + for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) { + String jcaPadding; + try { + jcaPadding = KeymasterUtils.getJcaEncryptionPaddingFromKeymasterPadding( + keymasterPadding); + } catch (IllegalArgumentException e) { + throw new InvalidKeySpecException( + "Unsupported encryption padding: " + keymasterPadding); + } + encryptionPaddingsList.add(jcaPadding); } + encryptionPaddings = + encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]); + + digests = KeymasterUtils.getJcaDigestAlgorithmsFromKeymasterDigests( + keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST)); + blockModes = KeymasterUtils.getJcaBlockModesFromKeymasterBlockModes( + keyCharacteristics.getInts(KeymasterDefs.KM_TAG_BLOCK_MODE)); + + @KeyStoreKeyProperties.UserAuthenticatorEnum + int swEnforcedKeymasterUserAuthenticators = + keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); + @KeyStoreKeyProperties.UserAuthenticatorEnum + int hwEnforcedKeymasterUserAuthenticators = + keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); + @KeyStoreKeyProperties.UserAuthenticatorEnum + int keymasterUserAuthenticators = + swEnforcedKeymasterUserAuthenticators | hwEnforcedKeymasterUserAuthenticators; + userAuthenticators = KeyStoreKeyProperties.UserAuthenticator.allFromKeymaster( + keymasterUserAuthenticators); + teeEnforcedUserAuthenticators = + KeyStoreKeyProperties.UserAuthenticator.allFromKeymaster( + hwEnforcedKeymasterUserAuthenticators); } catch (IllegalArgumentException e) { throw new InvalidKeySpecException("Unsupported key characteristic", e); } - Date keyValidityStart = - KeymasterUtils.getDate(keyCharacteristics, KeymasterDefs.KM_TAG_ACTIVE_DATETIME); + Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME); if ((keyValidityStart != null) && (keyValidityStart.getTime() <= 0)) { keyValidityStart = null; } - Date keyValidityForOriginationEnd = KeymasterUtils.getDate(keyCharacteristics, - KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME); + Date keyValidityForOriginationEnd = + keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME); if ((keyValidityForOriginationEnd != null) && (keyValidityForOriginationEnd.getTime() == Long.MAX_VALUE)) { keyValidityForOriginationEnd = null; } - Date keyValidityForConsumptionEnd = KeymasterUtils.getDate(keyCharacteristics, - KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME); + Date keyValidityForConsumptionEnd = + keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME); if ((keyValidityForConsumptionEnd != null) && (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); + int userAuthenticationValidityDurationSeconds = + keyCharacteristics.getInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1); return new KeyStoreKeySpec(entryAlias, + teeBacked, origin, keySize, keyValidityStart, keyValidityForOriginationEnd, 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), + encryptionPaddings, + EmptyArray.STRING, // no signature paddings -- this is symmetric crypto + digests, + blockModes, userAuthenticators, - teeBackedUserAuthenticators, - KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT)); + teeEnforcedUserAuthenticators, + userAuthenticationValidityDurationSeconds); } @Override diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java index 3143d4d..67f75c2 100644 --- a/keystore/java/android/security/KeymasterUtils.java +++ b/keystore/java/android/security/KeymasterUtils.java @@ -16,48 +16,327 @@ package android.security; -import android.security.keymaster.KeyCharacteristics; +import android.security.keymaster.KeymasterDefs; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import libcore.util.EmptyArray; + +import java.util.Collection; +import java.util.Locale; /** * @hide */ public abstract class KeymasterUtils { + private KeymasterUtils() {} - public static Integer getInt(KeyCharacteristics keyCharacteristics, int tag) { - if (keyCharacteristics.hwEnforced.containsTag(tag)) { - return keyCharacteristics.hwEnforced.getInt(tag, -1); - } else if (keyCharacteristics.swEnforced.containsTag(tag)) { - return keyCharacteristics.swEnforced.getInt(tag, -1); + public static int getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) { + if ("AES".equalsIgnoreCase(jcaKeyAlgorithm)) { + return KeymasterDefs.KM_ALGORITHM_AES; + } else if (jcaKeyAlgorithm.toUpperCase(Locale.US).startsWith("HMAC")) { + return KeymasterDefs.KM_ALGORITHM_HMAC; } else { - return null; + throw new IllegalArgumentException( + "Unsupported secret key algorithm: " + jcaKeyAlgorithm); + } + } + + public static String getJcaSecretKeyAlgorithm(int keymasterAlgorithm, int keymasterDigest) { + switch (keymasterAlgorithm) { + case KeymasterDefs.KM_ALGORITHM_AES: + if (keymasterDigest != -1) { + throw new IllegalArgumentException( + "Digest not supported for AES key: " + keymasterDigest); + } + return "AES"; + case KeymasterDefs.KM_ALGORITHM_HMAC: + switch (keymasterDigest) { + case KeymasterDefs.KM_DIGEST_SHA1: + return "HmacSHA1"; + case KeymasterDefs.KM_DIGEST_SHA_2_224: + return "HmacSHA224"; + case KeymasterDefs.KM_DIGEST_SHA_2_256: + return "HmacSHA256"; + case KeymasterDefs.KM_DIGEST_SHA_2_384: + return "HmacSHA384"; + case KeymasterDefs.KM_DIGEST_SHA_2_512: + return "HmacSHA512"; + default: + throw new IllegalArgumentException( + "Unsupported HMAC digest: " + keymasterDigest); + } + default: + throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm); } } - public static List<Integer> getInts(KeyCharacteristics keyCharacteristics, int tag) { - List<Integer> result = new ArrayList<Integer>(); - result.addAll(keyCharacteristics.hwEnforced.getInts(tag)); - result.addAll(keyCharacteristics.swEnforced.getInts(tag)); + public static String getJcaKeyPairAlgorithmFromKeymasterAlgorithm(int keymasterAlgorithm) { + switch (keymasterAlgorithm) { + case KeymasterDefs.KM_ALGORITHM_RSA: + return "RSA"; + case KeymasterDefs.KM_ALGORITHM_EC: + return "EC"; + default: + throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm); + } + } + + public static int getKeymasterDigestfromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) { + String algorithmUpper = jcaKeyAlgorithm.toUpperCase(Locale.US); + if (algorithmUpper.startsWith("HMAC")) { + String digestUpper = algorithmUpper.substring("HMAC".length()); + switch (digestUpper) { + 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("Unsupported HMAC digest: " + digestUpper); + } + } else { + return -1; + } + } + + public static int getKeymasterDigestFromJcaDigestAlgorithm(String jcaDigestAlgorithm) { + if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-1")) { + return KeymasterDefs.KM_DIGEST_SHA1; + } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-224")) { + return KeymasterDefs.KM_DIGEST_SHA_2_224; + } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-256")) { + return KeymasterDefs.KM_DIGEST_SHA_2_256; + } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-384")) { + return KeymasterDefs.KM_DIGEST_SHA_2_384; + } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-512")) { + return KeymasterDefs.KM_DIGEST_SHA_2_512; + } else if (jcaDigestAlgorithm.equalsIgnoreCase("NONE")) { + return KeymasterDefs.KM_DIGEST_NONE; + } else if (jcaDigestAlgorithm.equalsIgnoreCase("MD5")) { + return KeymasterDefs.KM_DIGEST_MD5; + } else { + throw new IllegalArgumentException( + "Unsupported digest algorithm: " + jcaDigestAlgorithm); + } + } + + public static String getJcaDigestAlgorithmFromKeymasterDigest(int keymasterDigest) { + switch (keymasterDigest) { + case KeymasterDefs.KM_DIGEST_NONE: + return "NONE"; + case KeymasterDefs.KM_DIGEST_MD5: + return "MD5"; + case KeymasterDefs.KM_DIGEST_SHA1: + return "SHA-1"; + case KeymasterDefs.KM_DIGEST_SHA_2_224: + return "SHA-224"; + case KeymasterDefs.KM_DIGEST_SHA_2_256: + return "SHA-256"; + case KeymasterDefs.KM_DIGEST_SHA_2_384: + return "SHA-384"; + case KeymasterDefs.KM_DIGEST_SHA_2_512: + return "SHA-512"; + default: + throw new IllegalArgumentException( + "Unsupported digest algorithm: " + keymasterDigest); + } + } + + public static String[] getJcaDigestAlgorithmsFromKeymasterDigests( + Collection<Integer> keymasterDigests) { + if (keymasterDigests.isEmpty()) { + return EmptyArray.STRING; + } + String[] result = new String[keymasterDigests.size()]; + int offset = 0; + for (int keymasterDigest : keymasterDigests) { + result[offset] = getJcaDigestAlgorithmFromKeymasterDigest(keymasterDigest); + offset++; + } return result; } - public static Date getDate(KeyCharacteristics keyCharacteristics, int tag) { - Date result = keyCharacteristics.hwEnforced.getDate(tag, null); - if (result == null) { - result = keyCharacteristics.swEnforced.getDate(tag, null); + public static int[] getKeymasterDigestsFromJcaDigestAlgorithms(String[] jcaDigestAlgorithms) { + if ((jcaDigestAlgorithms == null) || (jcaDigestAlgorithms.length == 0)) { + return EmptyArray.INT; + } + int[] result = new int[jcaDigestAlgorithms.length]; + int offset = 0; + for (String jcaDigestAlgorithm : jcaDigestAlgorithms) { + result[offset] = getKeymasterDigestFromJcaDigestAlgorithm(jcaDigestAlgorithm); + offset++; } return result; } - public static boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) { - if (keyCharacteristics.hwEnforced.containsTag(tag)) { - return keyCharacteristics.hwEnforced.getBoolean(tag, false); + public static int getDigestOutputSizeBytes(int keymasterDigest) { + switch (keymasterDigest) { + case KeymasterDefs.KM_DIGEST_NONE: + return -1; + case KeymasterDefs.KM_DIGEST_MD5: + return 128 / 8; + case KeymasterDefs.KM_DIGEST_SHA1: + return 160 / 8; + case KeymasterDefs.KM_DIGEST_SHA_2_224: + return 224 / 8; + case KeymasterDefs.KM_DIGEST_SHA_2_256: + return 256 / 8; + case KeymasterDefs.KM_DIGEST_SHA_2_384: + return 384 / 8; + case KeymasterDefs.KM_DIGEST_SHA_2_512: + return 512 / 8; + default: + throw new IllegalArgumentException("Unknown digest: " + keymasterDigest); + } + } + + public static int getKeymasterBlockModeFromJcaBlockMode(String jcaBlockMode) { + if ("ECB".equalsIgnoreCase(jcaBlockMode)) { + return KeymasterDefs.KM_MODE_ECB; + } else if ("CBC".equalsIgnoreCase(jcaBlockMode)) { + return KeymasterDefs.KM_MODE_CBC; + } else if ("CTR".equalsIgnoreCase(jcaBlockMode)) { + return KeymasterDefs.KM_MODE_CTR; + } else if ("GCM".equalsIgnoreCase(jcaBlockMode)) { + return KeymasterDefs.KM_MODE_GCM; } else { - return keyCharacteristics.swEnforced.getBoolean(tag, false); + throw new IllegalArgumentException("Unsupported block mode: " + jcaBlockMode); } } + + public static String getJcaBlockModeFromKeymasterBlockMode(int keymasterBlockMode) { + switch (keymasterBlockMode) { + case KeymasterDefs.KM_MODE_ECB: + return "ECB"; + case KeymasterDefs.KM_MODE_CBC: + return "CBC"; + case KeymasterDefs.KM_MODE_CTR: + return "CTR"; + case KeymasterDefs.KM_MODE_GCM: + return "GCM"; + default: + throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode); + } + } + + public static String[] getJcaBlockModesFromKeymasterBlockModes( + Collection<Integer> keymasterBlockModes) { + if ((keymasterBlockModes == null) || (keymasterBlockModes.isEmpty())) { + return EmptyArray.STRING; + } + String[] result = new String[keymasterBlockModes.size()]; + int offset = 0; + for (int keymasterBlockMode : keymasterBlockModes) { + result[offset] = getJcaBlockModeFromKeymasterBlockMode(keymasterBlockMode); + offset++; + } + return result; + } + + public static int[] getKeymasterBlockModesFromJcaBlockModes(String[] jcaBlockModes) { + if ((jcaBlockModes == null) || (jcaBlockModes.length == 0)) { + return EmptyArray.INT; + } + int[] result = new int[jcaBlockModes.length]; + for (int i = 0; i < jcaBlockModes.length; i++) { + result[i] = getKeymasterBlockModeFromJcaBlockMode(jcaBlockModes[i]); + } + return result; + } + + public static boolean isKeymasterBlockModeIndCpaCompatible(int keymasterBlockMode) { + switch (keymasterBlockMode) { + case KeymasterDefs.KM_MODE_ECB: + return false; + case KeymasterDefs.KM_MODE_CBC: + case KeymasterDefs.KM_MODE_CTR: + case KeymasterDefs.KM_MODE_GCM: + return true; + default: + throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode); + } + } + + public static int getKeymasterPaddingFromJcaEncryptionPadding(String jcaPadding) { + if ("NoPadding".equalsIgnoreCase(jcaPadding)) { + return KeymasterDefs.KM_PAD_NONE; + } else if ("PKCS7Padding".equalsIgnoreCase(jcaPadding)) { + return KeymasterDefs.KM_PAD_PKCS7; + } else if ("PKCS1Padding".equalsIgnoreCase(jcaPadding)) { + return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT; + } else if ("OEAPPadding".equalsIgnoreCase(jcaPadding)) { + return KeymasterDefs.KM_PAD_RSA_OAEP; + } else { + throw new IllegalArgumentException( + "Unsupported encryption padding scheme: " + jcaPadding); + } + } + + public static String getJcaEncryptionPaddingFromKeymasterPadding(int keymasterPadding) { + switch (keymasterPadding) { + case KeymasterDefs.KM_PAD_NONE: + return "NoPadding"; + case KeymasterDefs.KM_PAD_PKCS7: + return "PKCS7Padding"; + case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT: + return "PKCS1Padding"; + case KeymasterDefs.KM_PAD_RSA_OAEP: + return "OEAPPadding"; + default: + throw new IllegalArgumentException( + "Unsupported encryption padding: " + keymasterPadding); + } + } + + public static int getKeymasterPaddingFromJcaSignaturePadding(String jcaPadding) { + if ("PKCS#1".equalsIgnoreCase(jcaPadding)) { + return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN; + } if ("PSS".equalsIgnoreCase(jcaPadding)) { + return KeymasterDefs.KM_PAD_RSA_PSS; + } else { + throw new IllegalArgumentException( + "Unsupported signature padding scheme: " + jcaPadding); + } + } + + public static String getJcaSignaturePaddingFromKeymasterPadding(int keymasterPadding) { + switch (keymasterPadding) { + case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN: + return "PKCS#1"; + case KeymasterDefs.KM_PAD_RSA_PSS: + return "PSS"; + default: + throw new IllegalArgumentException( + "Unsupported signature padding: " + keymasterPadding); + } + } + + public static int[] getKeymasterPaddingsFromJcaEncryptionPaddings(String[] jcaPaddings) { + if ((jcaPaddings == null) || (jcaPaddings.length == 0)) { + return EmptyArray.INT; + } + int[] result = new int[jcaPaddings.length]; + for (int i = 0; i < jcaPaddings.length; i++) { + result[i] = getKeymasterPaddingFromJcaEncryptionPadding(jcaPaddings[i]); + } + return result; + } + + public static int[] getKeymasterPaddingsFromJcaSignaturePaddings(String[] jcaPaddings) { + if ((jcaPaddings == null) || (jcaPaddings.length == 0)) { + return EmptyArray.INT; + } + int[] result = new int[jcaPaddings.length]; + for (int i = 0; i < jcaPaddings.length; i++) { + result[i] = getKeymasterPaddingFromJcaSignaturePadding(jcaPaddings[i]); + } + return result; + } } diff --git a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java index bc8dd13..681a9ff 100644 --- a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java +++ b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java @@ -24,6 +24,11 @@ import java.util.Date; import javax.security.auth.x500.X500Principal; public class KeyPairGeneratorSpecTest extends AndroidTestCase { + private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake"); + private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1"); + private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1980 + private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048 + private static final String TEST_ALIAS_1 = "test1"; private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1"); @@ -105,46 +110,37 @@ public class KeyPairGeneratorSpecTest extends AndroidTestCase { } } - public void testConstructor_NullSubjectDN_Failure() throws Exception { - try { - new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, null, SERIAL_1, NOW, - NOW_PLUS_10_YEARS, 0); - fail("Should throw IllegalArgumentException when subjectDN is null"); - } catch (IllegalArgumentException success) { - } + public void testConstructor_NullSubjectDN_Success() throws Exception { + KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec( + getContext(), TEST_ALIAS_1, "RSA", 1024, null, null, SERIAL_1, NOW, + NOW_PLUS_10_YEARS, 0); + assertEquals(DEFAULT_CERT_SUBJECT, spec.getSubjectDN()); } - public void testConstructor_NullSerial_Failure() throws Exception { - try { - new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, null, NOW, - NOW_PLUS_10_YEARS, 0); - fail("Should throw IllegalArgumentException when startDate is null"); - } catch (IllegalArgumentException success) { - } + public void testConstructor_NullSerial_Success() throws Exception { + KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec( + getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, null, NOW, + NOW_PLUS_10_YEARS, 0); + assertEquals(DEFAULT_CERT_SERIAL_NUMBER, spec.getSerialNumber()); } - public void testConstructor_NullStartDate_Failure() throws Exception { - try { - new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, - null, NOW_PLUS_10_YEARS, 0); - fail("Should throw IllegalArgumentException when startDate is null"); - } catch (IllegalArgumentException success) { - } + public void testConstructor_NullStartDate_Success() throws Exception { + KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec( + getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, null, + NOW_PLUS_10_YEARS, 0); + assertEquals(DEFAULT_CERT_NOT_BEFORE, spec.getStartDate()); } - public void testConstructor_NullEndDate_Failure() throws Exception { - try { - new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, - NOW, null, 0); - fail("Should throw IllegalArgumentException when keystoreAlias is null"); - } catch (IllegalArgumentException success) { - } + public void testConstructor_NullEndDate_Success() throws Exception { + KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec( + getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, NOW, null, 0); + assertEquals(DEFAULT_CERT_NOT_AFTER, spec.getEndDate()); } public void testConstructor_EndBeforeStart_Failure() throws Exception { try { - new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, - NOW_PLUS_10_YEARS, NOW, 0); + new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, + SERIAL_1, NOW_PLUS_10_YEARS, NOW, 0); fail("Should throw IllegalArgumentException when end is before start"); } catch (IllegalArgumentException success) { } diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java index c9a140c..1a5552a 100644 --- a/keystore/tests/src/android/security/KeyStoreTest.java +++ b/keystore/tests/src/android/security/KeyStoreTest.java @@ -294,14 +294,14 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { public void testSaw_ungrantedUid_Bluetooth() throws Exception { String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID); - assertNull(results1); + assertEquals(0, results1.length); mKeyStore.password(TEST_PASSWD); mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED); mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED); String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID); - assertNull(results2); + assertEquals(0, results2.length); } public void testSaw_grantedUid_Wifi() throws Exception { @@ -798,7 +798,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { // TODO: Verify we have an RSA public key that's well formed. } - public void testAesOcbEncryptSuccess() throws Exception { + public void testAesGcmEncryptSuccess() throws Exception { String name = "test"; KeymasterArguments args = new KeymasterArguments(); args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT); @@ -806,7 +806,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256); - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB); + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM); args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096); args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16); args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); @@ -903,9 +903,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256); - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB); - args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096); - args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16); + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR); args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); KeyCharacteristics outCharacteristics = new KeyCharacteristics(); @@ -935,11 +933,9 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT); args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT); args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); - args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); + args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7); args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256); - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB); - args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096); - args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16); + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB); args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 1); KeyCharacteristics outCharacteristics = new KeyCharacteristics(); diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 5b5b098..c1b50c1 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -183,6 +183,8 @@ void FontRenderer::flushAllAndInvalidate() { for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) { mRGBACacheTextures[i]->init(); } + + mDrawn = false; } void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) { diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp index 830a13a..022820b 100644 --- a/libs/hwui/RenderBufferCache.cpp +++ b/libs/hwui/RenderBufferCache.cpp @@ -158,6 +158,11 @@ bool RenderBufferCache::put(RenderBuffer* buffer) { buffer->getWidth(), buffer->getHeight()); return true; + } else { + RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d) Size=%d, MaxSize=%d", + RenderBuffer::formatName(buffer->getFormat()), + buffer->getWidth(), buffer->getHeight(), size, mMaxSize); + delete buffer; } return false; } 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..9f3348c 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -130,8 +130,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 +144,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. * @@ -251,6 +232,11 @@ public class RenderScript { validate(); rsnContextSetPriority(mContext, p); } + native void rsnContextSetCacheDir(long con, String cacheDir); + synchronized void nContextSetCacheDir(String cacheDir) { + validate(); + rsnContextSetCacheDir(mContext, cacheDir); + } native void rsnContextDump(long con, int bits); synchronized void nContextDump(int bits) { validate(); @@ -346,10 +332,12 @@ public class RenderScript { rsnClosureSetGlobal(mContext, closureID, fieldID, value, size); } - native long rsnScriptGroup2Create(long con, String cachePath, long[] closures); - synchronized long nScriptGroup2Create(String cachePath, long[] closures) { + native long rsnScriptGroup2Create(long con, String name, String cachePath, + long[] closures); + synchronized long nScriptGroup2Create(String name, String cachePath, + long[] closures) { validate(); - return rsnScriptGroup2Create(mContext, cachePath, closures); + return rsnScriptGroup2Create(mContext, name, cachePath, closures); } native void rsnScriptGroup2Execute(long con, long groupID); @@ -1346,6 +1334,14 @@ public class RenderScript { if (rs.mContext == 0) { throw new RSDriverException("Failed to create RS context."); } + + // set up cache directory for entire context + final String CACHE_PATH = "com.android.renderscript.cache"; + File f = new File(RenderScriptCacheDir.mCacheDir, CACHE_PATH); + String mCachePath = f.getAbsolutePath(); + f.mkdirs(); + rs.nContextSetCacheDir(mCachePath); + rs.mMessageThread = new MessageThread(rs); rs.mMessageThread.start(); return rs; 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..417bbee 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; @@ -289,6 +289,7 @@ public class ScriptGroup2 extends BaseObj { } } + String mName; List<Closure> mClosures; List<UnboundValue> mInputs; Future[] mOutputs; @@ -299,9 +300,10 @@ public class ScriptGroup2 extends BaseObj { super(id, rs); } - ScriptGroup2(RenderScript rs, List<Closure> closures, + ScriptGroup2(RenderScript rs, String name, List<Closure> closures, List<UnboundValue> inputs, Future[] outputs) { super(0, rs); + mName = name; mClosures = closures; mInputs = inputs; mOutputs = outputs; @@ -310,7 +312,7 @@ public class ScriptGroup2 extends BaseObj { for (int i = 0; i < closureIDs.length; i++) { closureIDs[i] = closures.get(i).getID(rs); } - long id = rs.nScriptGroup2Create(ScriptC.mCachePath, closureIDs); + long id = rs.nScriptGroup2Create(name, ScriptC.mCachePath, closureIDs); setID(id); } @@ -412,8 +414,12 @@ public class ScriptGroup2 extends BaseObj { return addInvoke(invoke, args.toArray(), bindingMap); } - public ScriptGroup2 create(Future... outputs) { - ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs); + public ScriptGroup2 create(String name, Future... outputs) { + if (name == null || name.isEmpty() || name.length() > 100 || + !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) { + throw new RSIllegalArgumentException("invalid script group name"); + } + ScriptGroup2 ret = new ScriptGroup2(mRS, name, mClosures, mInputs, outputs); return ret; } 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/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 3591199..ba194ee 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -423,8 +423,9 @@ nClosureSetGlobal(JNIEnv *_env, jobject _this, jlong con, jlong closureID, } static long -nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, +nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name, jstring cacheDir, jlongArray closureArray) { + AutoJavaStringToUTF8 nameUTF(_env, name); AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir); jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr); @@ -435,7 +436,8 @@ nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, } return (jlong)(uintptr_t)rsScriptGroup2Create( - (RsContext)con, cacheDirUTF.c_str(), cacheDirUTF.length(), + (RsContext)con, nameUTF.c_str(), nameUTF.length(), + cacheDirUTF.c_str(), cacheDirUTF.length(), closures, numClosures); } @@ -689,6 +691,17 @@ nContextSetPriority(JNIEnv *_env, jobject _this, jlong con, jint p) rsContextSetPriority((RsContext)con, p); } +static void +nContextSetCacheDir(JNIEnv *_env, jobject _this, jlong con, jstring cacheDir) +{ + AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir); + + if (kLogApi) { + ALOGD("ContextSetCacheDir, con(%p), cacheDir(%s)", (RsContext)con, cacheDirUTF.c_str()); + } + rsContextSetCacheDir((RsContext)con, cacheDirUTF.c_str(), cacheDirUTF.length()); +} + static void @@ -2312,6 +2325,7 @@ static JNINativeMethod methods[] = { {"rsnContextCreateGL", "(JIIIIIIIIIIIIFI)J", (void*)nContextCreateGL }, {"rsnContextFinish", "(J)V", (void*)nContextFinish }, {"rsnContextSetPriority", "(JI)V", (void*)nContextSetPriority }, +{"rsnContextSetCacheDir", "(JLjava/lang/String;)V", (void*)nContextSetCacheDir }, {"rsnContextSetSurface", "(JIILandroid/view/Surface;)V", (void*)nContextSetSurface }, {"rsnContextDestroy", "(J)V", (void*)nContextDestroy }, {"rsnContextDump", "(JI)V", (void*)nContextDump }, @@ -2402,7 +2416,7 @@ static JNINativeMethod methods[] = { {"rsnScriptInvokeIDCreate", "(JJI)J", (void*)nScriptInvokeIDCreate }, {"rsnScriptFieldIDCreate", "(JJI)J", (void*)nScriptFieldIDCreate }, {"rsnScriptGroupCreate", "(J[J[J[J[J[J)J", (void*)nScriptGroupCreate }, -{"rsnScriptGroup2Create", "(JLjava/lang/String;[J)J", (void*)nScriptGroup2Create }, +{"rsnScriptGroup2Create", "(JLjava/lang/String;Ljava/lang/String;[J)J", (void*)nScriptGroup2Create }, {"rsnScriptGroupSetInput", "(JJJJ)V", (void*)nScriptGroupSetInput }, {"rsnScriptGroupSetOutput", "(JJJJ)V", (void*)nScriptGroupSetOutput }, {"rsnScriptGroupExecute", "(JJ)V", (void*)nScriptGroupExecute }, 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/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 09ebe60..05a4d7e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -91,6 +91,7 @@ import com.google.android.collect.Lists; import com.google.android.collect.Maps; import libcore.io.IoUtils; +import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -14163,7 +14164,7 @@ public final class ActivityManagerService extends ActivityManagerNative } else if ("-h".equals(opt)) { pw.println("meminfo dump options: [-a] [-d] [-c] [--oom] [process]"); pw.println(" -a: include all available information for each process."); - pw.println(" -d: include dalvik details when dumping process details."); + pw.println(" -d: include dalvik details."); pw.println(" -c: dump in a compact machine-parseable representation."); pw.println(" --oom: only show processes organized by oom adj."); pw.println(" --local: only collect details locally, don't call process."); @@ -14250,6 +14251,8 @@ public final class ActivityManagerService extends ActivityManagerNative final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>(); long nativePss = 0; long dalvikPss = 0; + long[] dalvikSubitemPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : + EmptyArray.LONG; long otherPss = 0; long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; @@ -14327,6 +14330,9 @@ public final class ActivityManagerService extends ActivityManagerNative nativePss += mi.nativePss; dalvikPss += mi.dalvikPss; + for (int j=0; j<dalvikSubitemPss.length; j++) { + dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); + } otherPss += mi.otherPss; for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { long mem = mi.getOtherPss(j); @@ -14385,6 +14391,10 @@ public final class ActivityManagerService extends ActivityManagerNative nativePss += mi.nativePss; dalvikPss += mi.dalvikPss; + for (int j=0; j<dalvikSubitemPss.length; j++) { + dalvikSubitemPss[j] += mi.getOtherPss( + Debug.MemoryInfo.NUM_OTHER_STATS + j); + } otherPss += mi.otherPss; for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { long mem = mi.getOtherPss(j); @@ -14403,7 +14413,16 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<MemItem> catMems = new ArrayList<MemItem>(); catMems.add(new MemItem("Native", "Native", nativePss, -1)); - catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, -2)); + final MemItem dalvikItem = new MemItem("Dalvik", "Dalvik", dalvikPss, -2); + if (dalvikSubitemPss.length > 0) { + dalvikItem.subitems = new ArrayList<MemItem>(); + for (int j=0; j<dalvikSubitemPss.length; j++) { + final String name = Debug.MemoryInfo.getOtherLabel( + Debug.MemoryInfo.NUM_OTHER_STATS + j); + dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], j)); + } + } + catMems.add(dalvikItem); catMems.add(new MemItem("Unknown", "Unknown", otherPss, -3)); for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { String label = Debug.MemoryInfo.getOtherLabel(j); @@ -15690,8 +15709,9 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized (this) { - if (callerApp != null && callerApp.pid == 0) { - // Caller already died + if (callerApp != null && (callerApp.thread == null + || callerApp.thread.asBinder() != caller.asBinder())) { + // Original caller already died return null; } ReceiverList rl 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..ce31f98 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -20,7 +20,9 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageStats; import android.os.Build; +import android.text.TextUtils; import android.util.Slog; + import dalvik.system.VMRuntime; import com.android.internal.os.InstallerConnection; @@ -42,9 +44,24 @@ public final class Installer extends SystemService { ping(); } + private static String escapeNull(String arg) { + if (TextUtils.isEmpty(arg)) { + return "!"; + } else { + return arg; + } + } + + @Deprecated public int install(String name, int uid, int gid, String seinfo) { + return install(null, name, uid, gid, seinfo); + } + + public int install(String uuid, String name, int uid, int gid, String seinfo) { StringBuilder builder = new StringBuilder("install"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(name); builder.append(' '); builder.append(uid); @@ -55,43 +72,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) { - 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) { + 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, 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); } @@ -146,9 +145,16 @@ public final class Installer extends SystemService { return mInstaller.execute(builder.toString()); } + @Deprecated public int remove(String name, int userId) { + return remove(null, name, userId); + } + + public int remove(String uuid, String name, int userId) { StringBuilder builder = new StringBuilder("remove"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(name); builder.append(' '); builder.append(userId); @@ -164,9 +170,16 @@ public final class Installer extends SystemService { return mInstaller.execute(builder.toString()); } + @Deprecated public int fixUid(String name, int uid, int gid) { + return fixUid(null, name, uid, gid); + } + + public int fixUid(String uuid, String name, int uid, int gid) { StringBuilder builder = new StringBuilder("fixuid"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(name); builder.append(' '); builder.append(uid); @@ -175,27 +188,48 @@ public final class Installer extends SystemService { return mInstaller.execute(builder.toString()); } + @Deprecated public int deleteCacheFiles(String name, int userId) { + return deleteCacheFiles(null, name, userId); + } + + public int deleteCacheFiles(String uuid, String name, int userId) { StringBuilder builder = new StringBuilder("rmcache"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(name); builder.append(' '); builder.append(userId); return mInstaller.execute(builder.toString()); } + @Deprecated public int deleteCodeCacheFiles(String name, int userId) { + return deleteCodeCacheFiles(null, name, userId); + } + + public int deleteCodeCacheFiles(String uuid, String name, int userId) { StringBuilder builder = new StringBuilder("rmcodecache"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(name); builder.append(' '); builder.append(userId); return mInstaller.execute(builder.toString()); } + @Deprecated public int createUserData(String name, int uid, int userId, String seinfo) { + return createUserData(null, name, uid, userId, seinfo); + } + + public int createUserData(String uuid, String name, int uid, int userId, String seinfo) { StringBuilder builder = new StringBuilder("mkuserdata"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(name); builder.append(' '); builder.append(uid); @@ -213,16 +247,30 @@ public final class Installer extends SystemService { return mInstaller.execute(builder.toString()); } + @Deprecated public int removeUserDataDirs(int userId) { + return removeUserDataDirs(null, userId); + } + + public int removeUserDataDirs(String uuid, int userId) { StringBuilder builder = new StringBuilder("rmuser"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(userId); return mInstaller.execute(builder.toString()); } + @Deprecated public int clearUserData(String name, int userId) { + return clearUserData(null, name, userId); + } + + public int clearUserData(String uuid, String name, int userId) { StringBuilder builder = new StringBuilder("rmuserdata"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(name); builder.append(' '); builder.append(userId); @@ -249,15 +297,30 @@ public final class Installer extends SystemService { } } + @Deprecated public int freeCache(long freeStorageSize) { + return freeCache(null, freeStorageSize); + } + + public int freeCache(String uuid, long freeStorageSize) { StringBuilder builder = new StringBuilder("freecache"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(String.valueOf(freeStorageSize)); return mInstaller.execute(builder.toString()); } + @Deprecated public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath, String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) { + return getSizeInfo(null, pkgName, persona, apkPath, libDirPath, fwdLockApkPath, asecPath, + instructionSets, pStats); + } + + public int getSizeInfo(String uuid, String pkgName, int persona, String apkPath, + String libDirPath, String fwdLockApkPath, String asecPath, String[] instructionSets, + PackageStats pStats) { for (String instructionSet : instructionSets) { if (!isValidInstructionSet(instructionSet)) { Slog.e(TAG, "Invalid instruction set: " + instructionSet); @@ -267,6 +330,8 @@ public final class Installer extends SystemService { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(pkgName); builder.append(' '); builder.append(persona); @@ -306,6 +371,11 @@ public final class Installer extends SystemService { return mInstaller.execute("movefiles"); } + @Deprecated + public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) { + return linkNativeLibraryDirectory(null, dataPath, nativeLibPath32, userId); + } + /** * Links the 32 bit native library directory in an application's data directory to the * real location for backward compatibility. Note that no such symlink is created for @@ -313,7 +383,8 @@ public final class Installer extends SystemService { * * @return -1 on error */ - public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) { + public int linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32, + int userId) { if (dataPath == null) { Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null"); return -1; @@ -322,7 +393,10 @@ public final class Installer extends SystemService { return -1; } - StringBuilder builder = new StringBuilder("linklib "); + StringBuilder builder = new StringBuilder("linklib"); + builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(dataPath); builder.append(' '); builder.append(nativeLibPath32); @@ -332,9 +406,16 @@ public final class Installer extends SystemService { return mInstaller.execute(builder.toString()); } + @Deprecated public boolean restoreconData(String pkgName, String seinfo, int uid) { + return restoreconData(null, pkgName, seinfo, uid); + } + + public boolean restoreconData(String uuid, String pkgName, String seinfo, int uid) { StringBuilder builder = new StringBuilder("restorecondata"); builder.append(' '); + builder.append(escapeNull(uuid)); + builder.append(' '); builder.append(pkgName); builder.append(' '); builder.append(seinfo != null ? seinfo : "!"); 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/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index 95ed7bc..d517642 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -37,6 +37,7 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -55,14 +56,15 @@ import org.xmlpull.v1.XmlPullParserException; */ public final class SELinuxMMAC { - private static final String TAG = "SELinuxMMAC"; + static final String TAG = "SELinuxMMAC"; private static final boolean DEBUG_POLICY = false; private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false; + private static final boolean DEBUG_POLICY_ORDER = DEBUG_POLICY || false; // All policy stanzas read from mac_permissions.xml. This is also the lock // to synchronize access during policy load and access attempts. - private static final List<Policy> sPolicies = new ArrayList<Policy>(); + private static List<Policy> sPolicies = new ArrayList<>(); // Data policy override version file. private static final String DATA_VERSION_FILE = @@ -115,17 +117,9 @@ public final class SELinuxMMAC { * were loaded successfully; no partial loading is possible. */ public static boolean readInstallPolicy() { - // Temp structure to hold the rules while we parse the xml file. We add - // all the rules once we know there's no problems. + // Temp structure to hold the rules while we parse the xml file List<Policy> policies = new ArrayList<>(); - // A separate structure to hold the default stanza. We need to add this to - // the end of the policies list structure. - Policy defaultPolicy = null; - - // Track sets of known policy certs so we can enforce rules across stanzas. - Set<Set<Signature>> knownCerts = new HashSet<>(); - FileReader policyFile = null; XmlPullParser parser = Xml.newPullParser(); try { @@ -141,31 +135,15 @@ public final class SELinuxMMAC { continue; } - String tagName = parser.getName(); - if ("signer".equals(tagName)) { - Policy signerPolicy = readSignerOrThrow(parser); - // Return of a Policy instance ensures certain invariants have - // passed, however, we still want to do some cross policy checking. - // Thus, check that we haven't seen the certs in another stanza. - Set<Signature> certs = signerPolicy.getSignatures(); - if (knownCerts.contains(certs)) { - String msg = "Separate stanzas have identical certs"; - throw new IllegalStateException(msg); - } - knownCerts.add(certs); - policies.add(signerPolicy); - } else if ("default".equals(tagName)) { - Policy defPolicy = readDefaultOrThrow(parser); - // Return of a Policy instance ensures certain invariants have - // passed, however, we still want to do some cross policy checking. - // Thus, check that we haven't already seen a default stanza. - if (defaultPolicy != null) { - String msg = "Multiple default stanzas identified"; - throw new IllegalStateException(msg); - } - defaultPolicy = defPolicy; - } else { - skip(parser); + switch (parser.getName()) { + case "signer": + policies.add(readSignerOrThrow(parser)); + break; + case "default": + policies.add(readDefaultOrThrow(parser)); + break; + default: + skip(parser); } } } catch (IllegalStateException | IllegalArgumentException | @@ -185,15 +163,22 @@ public final class SELinuxMMAC { IoUtils.closeQuietly(policyFile); } - // Add the default policy to the end if there is one. This will ensure that - // the default stanza is consulted last when performing policy lookups. - if (defaultPolicy != null) { - policies.add(defaultPolicy); + // Now sort the policy stanzas + PolicyComparator policySort = new PolicyComparator(); + Collections.sort(policies, policySort); + if (policySort.foundDuplicate()) { + Slog.w(TAG, "ERROR! Duplicate entries found parsing " + MAC_PERMISSIONS); + return false; } synchronized (sPolicies) { - sPolicies.clear(); - sPolicies.addAll(policies); + sPolicies = policies; + + if (DEBUG_POLICY_ORDER) { + for (Policy policy : sPolicies) { + Slog.d(TAG, "Policy: " + policy.toString()); + } + } } return true; @@ -497,30 +482,42 @@ public final class SELinuxMMAC { * of invariants before being built and returned. Each instance can be guaranteed to * hold one valid policy stanza as outlined in the external/sepolicy/mac_permissions.xml * file. - * </p> + * <p> * The following is an example of how to use {@link Policy.PolicyBuilder} to create a - * signer based Policy instance. + * signer based Policy instance with only inner package name refinements. * </p> * <pre> * {@code * Policy policy = new Policy.PolicyBuilder() * .addSignature("308204a8...") * .addSignature("483538c8...") - * .setGlobalSeinfoOrThrow("paltform") * .addInnerPackageMapOrThrow("com.foo.", "bar") * .addInnerPackageMapOrThrow("com.foo.other", "bar") * .build(); * } * </pre> * <p> - * An example of how to use {@link Policy.PolicyBuilder} to create a default based Policy - * instance. + * The following is an example of how to use {@link Policy.PolicyBuilder} to create a + * signer based Policy instance with only a global seinfo tag. + * </p> + * <pre> + * {@code + * Policy policy = new Policy.PolicyBuilder() + * .addSignature("308204a8...") + * .addSignature("483538c8...") + * .setGlobalSeinfoOrThrow("paltform") + * .build(); + * } + * </pre> + * <p> + * The following is an example of how to use {@link Policy.PolicyBuilder} to create a + * default based Policy instance. * </p> * <pre> * {@code * Policy policy = new Policy.PolicyBuilder() * .setAsDefaultPolicy() - * .setGlobalSeinfoOrThrow("defualt") + * .setGlobalSeinfoOrThrow("default") * .build(); * } * </pre> @@ -551,6 +548,65 @@ final class Policy { } /** + * Return whether this policy object represents a default stanza. + * + * @return A boolean indicating if this object represents a default policy stanza. + */ + public boolean isDefaultStanza() { + return mDefaultStanza; + } + + /** + * Return whether this policy object contains package name mapping refinements. + * + * @return A boolean indicating if this object has inner package name mappings. + */ + public boolean hasInnerPackages() { + return !mPkgMap.isEmpty(); + } + + /** + * Return the mapping of all package name refinements. + * + * @return A Map object whose keys are the package names and whose values are + * the seinfo assignments. + */ + public Map<String, String> getInnerPackages() { + return mPkgMap; + } + + /** + * Return whether the policy object has a global seinfo tag attached. + * + * @return A boolean indicating if this stanza has a global seinfo tag. + */ + public boolean hasGlobalSeinfo() { + return mSeinfo != null; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (mDefaultStanza) { + sb.append("defaultStanza=true "); + } + + for (Signature cert : mCerts) { + sb.append("cert=" + cert.toCharsString().substring(0, 11) + "... "); + } + + if (mSeinfo != null) { + sb.append("seinfo=" + mSeinfo); + } + + for (String name : mPkgMap.keySet()) { + sb.append(" " + name + "=" + mPkgMap.get(name)); + } + + return sb.toString(); + } + + /** * <p> * Determine the seinfo value to assign to an apk. The appropriate seinfo value * is determined using the following steps: @@ -623,7 +679,7 @@ final class Policy { } /** - * Sets this stanza as a defualt stanza. All policy stanzas are assumed to + * Sets this stanza as a default stanza. All policy stanzas are assumed to * be signer stanzas unless this method is explicitly called. Default stanzas * are treated differently with respect to allowable child tags, ordering and * when and how policy decisions are enforced. @@ -757,7 +813,7 @@ final class Policy { * <ul> * <li> at least one cert must be found </li> * <li> either a global seinfo value is present OR at least one - * inner package mapping must be present. </li> + * inner package mapping must be present BUT not both. </li> * </ul> * </li> * </ul> @@ -786,9 +842,9 @@ final class Policy { String err = "Missing certs with signer tag. Expecting at least one."; throw new IllegalStateException(err); } - if ((p.mSeinfo == null) && (p.mPkgMap.isEmpty())) { - String err = "Missing seinfo OR package tags with signer tag. At " + - "least one must be present."; + if (!(p.mSeinfo == null ^ p.mPkgMap.isEmpty())) { + String err = "Only seinfo tag XOR package tags are allowed within " + + "a signer stanza."; throw new IllegalStateException(err); } } @@ -797,3 +853,58 @@ final class Policy { } } } + +/** + * Comparision imposing an ordering on Policy objects. It is understood that Policy + * objects can only take one of three forms and ordered according to the following + * set of rules most specific to least. + * <ul> + * <li> signer stanzas with inner package mappings </li> + * <li> signer stanzas with global seinfo tags </li> + * <li> default stanza </li> + * </ul> + * This comparison also checks for duplicate entries on the input selectors. Any + * found duplicates will be flagged and can be checked with {@link #foundDuplicate}. + */ + +final class PolicyComparator implements Comparator<Policy> { + + private boolean duplicateFound = false; + + public boolean foundDuplicate() { + return duplicateFound; + } + + @Override + public int compare(Policy p1, Policy p2) { + + // Give precedence to signature stanzas over default stanzas + if (p1.isDefaultStanza() != p2.isDefaultStanza()) { + return p1.isDefaultStanza() ? 1 : -1; + } + + // Give precedence to stanzas with inner package mappings + if (p1.hasInnerPackages() != p2.hasInnerPackages()) { + return p1.hasInnerPackages() ? -1 : 1; + } + + // Check for duplicate entries + if (p1.getSignatures().equals(p2.getSignatures())) { + // Checks if default stanza or a signer w/o inner package names + if (p1.hasGlobalSeinfo()) { + duplicateFound = true; + Slog.e(SELinuxMMAC.TAG, "Duplicate policy entry: " + p1.toString()); + } + + // Look for common inner package name mappings + final Map<String, String> p1Packages = p1.getInnerPackages(); + final Map<String, String> p2Packages = p2.getInnerPackages(); + if (!Collections.disjoint(p1Packages.keySet(), p2Packages.keySet())) { + duplicateFound = true; + Slog.e(SELinuxMMAC.TAG, "Duplicate policy entry: " + p1.toString()); + } + } + + return 0; + } +} 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) { |