Merge "Delete a bunch of dead code" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 7df09a1..cdc2404 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28506,6 +28506,53 @@
public abstract class KeyStoreKeyProperties {
}
+ public static abstract class KeyStoreKeyProperties.Algorithm {
+ field public static final java.lang.String AES = "AES";
+ field public static final java.lang.String EC = "EC";
+ field public static final java.lang.String HMAC_SHA1 = "HmacSHA1";
+ field public static final java.lang.String HMAC_SHA224 = "HmacSHA224";
+ field public static final java.lang.String HMAC_SHA256 = "HmacSHA256";
+ field public static final java.lang.String HMAC_SHA384 = "HmacSHA384";
+ field public static final java.lang.String HMAC_SHA512 = "HmacSHA512";
+ field public static final java.lang.String RSA = "RSA";
+ }
+
+ public static abstract class KeyStoreKeyProperties.AlgorithmEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockMode {
+ field public static final java.lang.String CBC = "CBC";
+ field public static final java.lang.String CTR = "CTR";
+ field public static final java.lang.String ECB = "ECB";
+ field public static final java.lang.String GCM = "GCM";
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockModeEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.Digest {
+ field public static final java.lang.String MD5 = "MD5";
+ field public static final java.lang.String NONE = "NONE";
+ field public static final java.lang.String SHA1 = "SHA-1";
+ field public static final java.lang.String SHA224 = "SHA-224";
+ field public static final java.lang.String SHA256 = "SHA-256";
+ field public static final java.lang.String SHA384 = "SHA-384";
+ field public static final java.lang.String SHA512 = "SHA-512";
+ }
+
+ public static abstract class KeyStoreKeyProperties.DigestEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPadding {
+ field public static final java.lang.String NONE = "NoPadding";
+ field public static final java.lang.String PKCS7 = "PKCS7Padding";
+ field public static final java.lang.String RSA_OAEP = "OAEPPadding";
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1Padding";
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public static abstract class KeyStoreKeyProperties.Origin {
field public static final int GENERATED = 1; // 0x1
field public static final int IMPORTED = 2; // 0x2
@@ -28525,6 +28572,14 @@
public static abstract class KeyStoreKeyProperties.PurposeEnum implements java.lang.annotation.Annotation {
}
+ public static abstract class KeyStoreKeyProperties.SignaturePadding {
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1";
+ field public static final java.lang.String RSA_PSS = "PSS";
+ }
+
+ public static abstract class KeyStoreKeyProperties.SignaturePaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public class KeyStoreKeySpec implements java.security.spec.KeySpec {
method public java.lang.String[] getBlockModes();
method public java.lang.String[] getDigests();
diff --git a/api/system-current.txt b/api/system-current.txt
index 0662d93..7012773 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -30520,6 +30520,53 @@
public abstract class KeyStoreKeyProperties {
}
+ public static abstract class KeyStoreKeyProperties.Algorithm {
+ field public static final java.lang.String AES = "AES";
+ field public static final java.lang.String EC = "EC";
+ field public static final java.lang.String HMAC_SHA1 = "HmacSHA1";
+ field public static final java.lang.String HMAC_SHA224 = "HmacSHA224";
+ field public static final java.lang.String HMAC_SHA256 = "HmacSHA256";
+ field public static final java.lang.String HMAC_SHA384 = "HmacSHA384";
+ field public static final java.lang.String HMAC_SHA512 = "HmacSHA512";
+ field public static final java.lang.String RSA = "RSA";
+ }
+
+ public static abstract class KeyStoreKeyProperties.AlgorithmEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockMode {
+ field public static final java.lang.String CBC = "CBC";
+ field public static final java.lang.String CTR = "CTR";
+ field public static final java.lang.String ECB = "ECB";
+ field public static final java.lang.String GCM = "GCM";
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockModeEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.Digest {
+ field public static final java.lang.String MD5 = "MD5";
+ field public static final java.lang.String NONE = "NONE";
+ field public static final java.lang.String SHA1 = "SHA-1";
+ field public static final java.lang.String SHA224 = "SHA-224";
+ field public static final java.lang.String SHA256 = "SHA-256";
+ field public static final java.lang.String SHA384 = "SHA-384";
+ field public static final java.lang.String SHA512 = "SHA-512";
+ }
+
+ public static abstract class KeyStoreKeyProperties.DigestEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPadding {
+ field public static final java.lang.String NONE = "NoPadding";
+ field public static final java.lang.String PKCS7 = "PKCS7Padding";
+ field public static final java.lang.String RSA_OAEP = "OAEPPadding";
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1Padding";
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public static abstract class KeyStoreKeyProperties.Origin {
field public static final int GENERATED = 1; // 0x1
field public static final int IMPORTED = 2; // 0x2
@@ -30539,6 +30586,14 @@
public static abstract class KeyStoreKeyProperties.PurposeEnum implements java.lang.annotation.Annotation {
}
+ public static abstract class KeyStoreKeyProperties.SignaturePadding {
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1";
+ field public static final java.lang.String RSA_PSS = "PSS";
+ }
+
+ public static abstract class KeyStoreKeyProperties.SignaturePaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public class KeyStoreKeySpec implements java.security.spec.KeySpec {
method public java.lang.String[] getBlockModes();
method public java.lang.String[] getDigests();
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index bb25ec6..21dc1e2 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -630,7 +630,10 @@
}
glDisable(GL_SCISSOR_TEST);
}
- glDrawTexiOES(xc, yc, 0, animation.width, animation.height);
+ // specify the y center as ceiling((mHeight - animation.height) / 2)
+ // which is equivalent to mHeight - (yc + animation.height)
+ glDrawTexiOES(xc, mHeight - (yc + animation.height),
+ 0, animation.width, animation.height);
eglSwapBuffers(mDisplay, mSurface);
nsecs_t now = systemTime();
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 5790682..cdd72be 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -28,6 +28,7 @@
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
/**
* This class enables automatic animations on layout changes in ViewGroup objects. To enable
@@ -757,7 +758,7 @@
// reset the inter-animation delay, in case we use it later
staggerDelay = 0;
- final ViewTreeObserver observer = parent.getViewTreeObserver(); // used for later cleanup
+ final ViewTreeObserver observer = parent.getViewTreeObserver();
if (!observer.isAlive()) {
// If the observer's not in a good state, skip the transition
return;
@@ -790,21 +791,9 @@
// This is the cleanup step. When we get this rendering event, we know that all of
// the appropriate animations have been set up and run. Now we can clear out the
// layout listeners.
- observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- public boolean onPreDraw() {
- parent.getViewTreeObserver().removeOnPreDrawListener(this);
- int count = layoutChangeListenerMap.size();
- if (count > 0) {
- Collection<View> views = layoutChangeListenerMap.keySet();
- for (View view : views) {
- View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
- view.removeOnLayoutChangeListener(listener);
- }
- }
- layoutChangeListenerMap.clear();
- return true;
- }
- });
+ CleanupCallback callback = new CleanupCallback(layoutChangeListenerMap, parent);
+ observer.addOnPreDrawListener(callback);
+ parent.addOnAttachStateChangeListener(callback);
}
/**
@@ -1499,4 +1488,50 @@
View view, int transitionType);
}
+ /**
+ * Utility class to clean up listeners after animations are setup. Cleanup happens
+ * when either the OnPreDrawListener method is called or when the parent is detached,
+ * whichever comes first.
+ */
+ private static final class CleanupCallback implements ViewTreeObserver.OnPreDrawListener,
+ View.OnAttachStateChangeListener {
+
+ final Map<View, View.OnLayoutChangeListener> layoutChangeListenerMap;
+ final ViewGroup parent;
+
+ CleanupCallback(Map<View, View.OnLayoutChangeListener> listenerMap, ViewGroup parent) {
+ this.layoutChangeListenerMap = listenerMap;
+ this.parent = parent;
+ }
+
+ private void cleanup() {
+ parent.getViewTreeObserver().removeOnPreDrawListener(this);
+ parent.removeOnAttachStateChangeListener(this);
+ int count = layoutChangeListenerMap.size();
+ if (count > 0) {
+ Collection<View> views = layoutChangeListenerMap.keySet();
+ for (View view : views) {
+ View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
+ view.removeOnLayoutChangeListener(listener);
+ }
+ layoutChangeListenerMap.clear();
+ }
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ cleanup();
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ cleanup();
+ return true;
+ }
+ };
+
}
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 2939322..968c956 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -476,9 +476,8 @@
tempRect.set(0, 0, width, height);
view.getMatrix().mapRect(tempRect);
- ViewGroup parent = (ViewGroup) view.getParent();
- left = leftInParent - tempRect.left + parent.getScrollX();
- top = topInParent - tempRect.top + parent.getScrollY();
+ left = leftInParent - tempRect.left;
+ top = topInParent - tempRect.top;
right = left + width;
bottom = top + height;
}
@@ -506,7 +505,7 @@
ViewGroup parent = (ViewGroup) view.getParent();
Matrix matrix = new Matrix();
parent.transformMatrixToLocal(matrix);
-
+ matrix.postTranslate(parent.getScrollX(), parent.getScrollY());
mSharedElementParentMatrices.add(matrix);
}
}
@@ -521,6 +520,7 @@
// Find the location in the view's parent
ViewGroup parent = (ViewGroup) viewParent;
parent.transformMatrixToLocal(matrix);
+ matrix.postTranslate(parent.getScrollX(), parent.getScrollY());
}
} else {
// The indices of mSharedElementParentMatrices matches the
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index cf29310..3eeb04a 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1481,14 +1481,8 @@
// interface).
// Sanitize locale using isLanguageAvailable.
- int result = service.isLanguageAvailable( language, country, variant);
- if (result >= LANG_AVAILABLE){
- if (result < LANG_COUNTRY_VAR_AVAILABLE) {
- variant = "";
- if (result < LANG_COUNTRY_AVAILABLE) {
- country = "";
- }
- }
+ int result = service.isLanguageAvailable(language, country, variant);
+ if (result >= LANG_AVAILABLE) {
// Get the default voice for the locale.
String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
if (TextUtils.isEmpty(voiceName)) {
@@ -1502,10 +1496,28 @@
return LANG_NOT_SUPPORTED;
}
+ // Set the language/country/variant of the voice, so #getLanguage will return
+ // the currently set voice locale when called.
+ Voice voice = getVoice(service, voiceName);
+ String voiceLanguage = "";
+ try {
+ voiceLanguage = voice.getLocale().getISO3Language();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
+ voice.getLocale(), e);
+ }
+
+ String voiceCountry = "";
+ try {
+ voiceCountry = voice.getLocale().getISO3Country();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
+ voice.getLocale(), e);
+ }
mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
- mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
- mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
- mParams.putString(Engine.KEY_PARAM_VARIANT, variant);
+ mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage);
+ mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry);
+ mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
}
return result;
}
@@ -1654,20 +1666,32 @@
if (TextUtils.isEmpty(voiceName)) {
return null;
}
- List<Voice> voices = service.getVoices();
- if (voices == null) {
- return null;
- }
- for (Voice voice : voices) {
- if (voice.getName().equals(voiceName)) {
- return voice;
- }
- }
- return null;
+ return getVoice(service, voiceName);
}
}, null, "getVoice");
}
+
+ /**
+ * Returns a Voice instance of the voice with the given voice name.
+ *
+ * @return Voice instance with the given voice name, or {@code null} if not set or on error.
+ *
+ * @see Voice
+ */
+ private Voice getVoice(ITextToSpeechService service, String voiceName) throws RemoteException {
+ List<Voice> voices = service.getVoices();
+ if (voices == null) {
+ return null;
+ }
+ for (Voice voice : voices) {
+ if (voice.getName().equals(voiceName)) {
+ return voice;
+ }
+ }
+ return null;
+ }
+
/**
* Returns a Voice instance that's the default voice for the default Text-to-speech language.
* @return The default voice instance for the default language, or {@code null} if not set or
@@ -1690,14 +1714,7 @@
// Sanitize the locale using isLanguageAvailable.
int result = service.isLanguageAvailable(language, country, variant);
- if (result >= LANG_AVAILABLE){
- if (result < LANG_COUNTRY_VAR_AVAILABLE) {
- variant = "";
- if (result < LANG_COUNTRY_AVAILABLE) {
- country = "";
- }
- }
- } else {
+ if (result < LANG_AVAILABLE) {
// The default language is not supported.
return null;
}
diff --git a/core/java/com/android/internal/transition/EpicenterClipReveal.java b/core/java/com/android/internal/transition/EpicenterClipReveal.java
deleted file mode 100644
index 1a6736a..0000000
--- a/core/java/com/android/internal/transition/EpicenterClipReveal.java
+++ /dev/null
@@ -1,233 +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 com.android.internal.transition;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.RectEvaluator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.view.animation.PathInterpolator;
-
-import com.android.internal.R;
-
-/**
- * EpicenterClipReveal captures the {@link View#getClipBounds()} before and
- * after the scene change and animates between those and the epicenter bounds
- * during a visibility transition.
- */
-public class EpicenterClipReveal extends Visibility {
- private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
- private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
-
- private final TimeInterpolator mInterpolatorX;
- private final TimeInterpolator mInterpolatorY;
- private final boolean mCenterClipBounds;
-
- public EpicenterClipReveal() {
- mInterpolatorX = null;
- mInterpolatorY = null;
- mCenterClipBounds = false;
- }
-
- public EpicenterClipReveal(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.EpicenterClipReveal, 0, 0);
-
- mCenterClipBounds = a.getBoolean(R.styleable.EpicenterClipReveal_centerClipBounds, false);
-
- final int interpolatorX = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorX, 0);
- if (interpolatorX != 0) {
- mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
- } else {
- mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN;
- }
-
- final int interpolatorY = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorY, 0);
- if (interpolatorY != 0) {
- mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
- } else {
- mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- a.recycle();
- }
-
- @Override
- public void captureStartValues(TransitionValues transitionValues) {
- super.captureStartValues(transitionValues);
- captureValues(transitionValues);
- }
-
- @Override
- public void captureEndValues(TransitionValues transitionValues) {
- super.captureEndValues(transitionValues);
- captureValues(transitionValues);
- }
-
- private void captureValues(TransitionValues values) {
- final View view = values.view;
- if (view.getVisibility() == View.GONE) {
- return;
- }
-
- final Rect clip = view.getClipBounds();
- values.values.put(PROPNAME_CLIP, clip);
-
- if (clip == null) {
- final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
- values.values.put(PROPNAME_BOUNDS, bounds);
- }
- }
-
- @Override
- public Animator onAppear(ViewGroup sceneRoot, View view,
- TransitionValues startValues, TransitionValues endValues) {
- if (endValues == null) {
- return null;
- }
-
- final Rect end = getBestRect(endValues);
- final Rect start = getEpicenterOrCenter(end);
-
- // Prepare the view.
- view.setClipBounds(start);
-
- return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY);
- }
-
- @Override
- public Animator onDisappear(ViewGroup sceneRoot, View view,
- TransitionValues startValues, TransitionValues endValues) {
- if (startValues == null) {
- return null;
- }
-
- final Rect start = getBestRect(startValues);
- final Rect end = getEpicenterOrCenter(start);
-
- // Prepare the view.
- view.setClipBounds(start);
-
- return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY);
- }
-
- private Rect getEpicenterOrCenter(Rect bestRect) {
- final Rect epicenter = getEpicenter();
- if (epicenter != null) {
- // Translate the clip bounds to be centered within the target bounds.
- if (mCenterClipBounds) {
- final int offsetX = bestRect.centerX() - epicenter.centerX();
- final int offsetY = bestRect.centerY() - epicenter.centerY();
- epicenter.offset(offsetX, offsetY);
- }
- return epicenter;
- }
-
- final int centerX = bestRect.centerX();
- final int centerY = bestRect.centerY();
- return new Rect(centerX, centerY, centerX, centerY);
- }
-
- private Rect getBestRect(TransitionValues values) {
- final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
- if (clipRect == null) {
- return (Rect) values.values.get(PROPNAME_BOUNDS);
- }
- return clipRect;
- }
-
- private static Animator createRectAnimator(final View view, Rect start, Rect end,
- TransitionValues endValues, TimeInterpolator interpolatorX,
- TimeInterpolator interpolatorY) {
- final RectEvaluator evaluator = new RectEvaluator(new Rect());
- final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);
-
- final ClipDimenProperty propX = new ClipDimenProperty(ClipDimenProperty.TARGET_X);
- final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, start, end);
- if (interpolatorX != null) {
- animX.setInterpolator(interpolatorX);
- }
-
- final ClipDimenProperty propY = new ClipDimenProperty(ClipDimenProperty.TARGET_Y);
- final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, start, end);
- if (interpolatorY != null) {
- animY.setInterpolator(interpolatorY);
- }
-
- final AnimatorSet animSet = new AnimatorSet();
- animSet.playTogether(animX, animY);
- animSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setClipBounds(terminalClip);
- }
- });
- return animSet;
- }
-
- private static class ClipDimenProperty extends Property<View, Rect> {
- public static final char TARGET_X = 'x';
- public static final char TARGET_Y = 'y';
-
- private final Rect mTempRect = new Rect();
-
- private final int mTargetDimension;
-
- public ClipDimenProperty(char targetDimension) {
- super(Rect.class, "clip_bounds_" + targetDimension);
-
- mTargetDimension = targetDimension;
- }
-
- @Override
- public Rect get(View object) {
- final Rect tempRect = mTempRect;
- if (!object.getClipBounds(tempRect)) {
- tempRect.setEmpty();
- }
- return tempRect;
- }
-
- @Override
- public void set(View object, Rect value) {
- final Rect tempRect = mTempRect;
- if (object.getClipBounds(tempRect)) {
- if (mTargetDimension == TARGET_X) {
- tempRect.left = value.left;
- tempRect.right = value.right;
- } else {
- tempRect.top = value.top;
- tempRect.bottom = value.bottom;
- }
- object.setClipBounds(tempRect);
- }
- }
- }
-}
diff --git a/core/java/com/android/internal/transition/EpicenterTranslate.java b/core/java/com/android/internal/transition/EpicenterTranslate.java
deleted file mode 100644
index eac3ff8..0000000
--- a/core/java/com/android/internal/transition/EpicenterTranslate.java
+++ /dev/null
@@ -1,191 +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 com.android.internal.transition;
-
-import com.android.internal.R;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-
-/**
- * EpicenterTranslate captures the {@link View#getTranslationX()} and
- * {@link View#getTranslationY()} before and after the scene change and
- * animates between those and the epicenter's center during a visibility
- * transition.
- */
-public class EpicenterTranslate extends Visibility {
- private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
- private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX";
- private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY";
- private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ";
- private static final String PROPNAME_Z = "android:epicenterReveal:z";
-
- private final TimeInterpolator mInterpolatorX;
- private final TimeInterpolator mInterpolatorY;
- private final TimeInterpolator mInterpolatorZ;
-
- public EpicenterTranslate() {
- mInterpolatorX = null;
- mInterpolatorY = null;
- mInterpolatorZ = null;
- }
-
- public EpicenterTranslate(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.EpicenterTranslate, 0, 0);
-
- final int interpolatorX = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorX, 0);
- if (interpolatorX != 0) {
- mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
- } else {
- mInterpolatorX = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- final int interpolatorY = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorY, 0);
- if (interpolatorY != 0) {
- mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
- } else {
- mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- final int interpolatorZ = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorZ, 0);
- if (interpolatorZ != 0) {
- mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ);
- } else {
- mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- a.recycle();
- }
-
- @Override
- public void captureStartValues(TransitionValues transitionValues) {
- super.captureStartValues(transitionValues);
- captureValues(transitionValues);
- }
-
- @Override
- public void captureEndValues(TransitionValues transitionValues) {
- super.captureEndValues(transitionValues);
- captureValues(transitionValues);
- }
-
- private void captureValues(TransitionValues values) {
- final View view = values.view;
- if (view.getVisibility() == View.GONE) {
- return;
- }
-
- final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
- values.values.put(PROPNAME_BOUNDS, bounds);
- values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX());
- values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY());
- values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ());
- values.values.put(PROPNAME_Z, view.getZ());
- }
-
- @Override
- public Animator onAppear(ViewGroup sceneRoot, View view,
- TransitionValues startValues, TransitionValues endValues) {
- if (endValues == null) {
- return null;
- }
-
- final Rect end = (Rect) endValues.values.get(PROPNAME_BOUNDS);
- final Rect start = getEpicenterOrCenter(end);
- final float startX = start.centerX() - end.centerX();
- final float startY = start.centerY() - end.centerY();
- final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z);
-
- // Translate the view to be centered on the epicenter.
- view.setTranslationX(startX);
- view.setTranslationY(startY);
- view.setTranslationZ(startZ);
-
- final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
- final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
- final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
- return createAnimator(view, startX, startY, startZ, endX, endY, endZ,
- mInterpolatorX, mInterpolatorY, mInterpolatorZ);
- }
-
- @Override
- public Animator onDisappear(ViewGroup sceneRoot, View view,
- TransitionValues startValues, TransitionValues endValues) {
- if (startValues == null) {
- return null;
- }
-
- final Rect start = (Rect) endValues.values.get(PROPNAME_BOUNDS);
- final Rect end = getEpicenterOrCenter(start);
- final float endX = end.centerX() - start.centerX();
- final float endY = end.centerY() - start.centerY();
- final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z);
-
- final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
- final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
- final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
- return createAnimator(view, startX, startY, startZ, endX, endY, endZ,
- mInterpolatorX, mInterpolatorY, mInterpolatorZ);
- }
-
- private Rect getEpicenterOrCenter(Rect bestRect) {
- final Rect epicenter = getEpicenter();
- if (epicenter != null) {
- return epicenter;
- }
-
- final int centerX = bestRect.centerX();
- final int centerY = bestRect.centerY();
- return new Rect(centerX, centerY, centerX, centerY);
- }
-
- private static Animator createAnimator(final View view, float startX, float startY,
- float startZ, float endX, float endY, float endZ, TimeInterpolator interpolatorX,
- TimeInterpolator interpolatorY, TimeInterpolator interpolatorZ) {
- final ObjectAnimator animX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, startX, endX);
- if (interpolatorX != null) {
- animX.setInterpolator(interpolatorX);
- }
-
- final ObjectAnimator animY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY);
- if (interpolatorY != null) {
- animY.setInterpolator(interpolatorY);
- }
-
- final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ);
- if (interpolatorZ != null) {
- animZ.setInterpolator(interpolatorZ);
- }
-
- final AnimatorSet animSet = new AnimatorSet();
- animSet.playTogether(animX, animY, animZ);
- return animSet;
- }
-}
diff --git a/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java b/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java
new file mode 100644
index 0000000..2c10297
--- /dev/null
+++ b/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java
@@ -0,0 +1,328 @@
+/*
+ * 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 com.android.internal.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.TypeEvaluator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+
+import com.android.internal.R;
+
+/**
+ * EpicenterTranslateClipReveal captures the clip bounds and translation values
+ * before and after the scene change and animates between those and the
+ * epicenter bounds during a visibility transition.
+ */
+public class EpicenterTranslateClipReveal extends Visibility {
+ private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
+ private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
+ private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX";
+ private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY";
+ private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ";
+ private static final String PROPNAME_Z = "android:epicenterReveal:z";
+
+ private final TimeInterpolator mInterpolatorX;
+ private final TimeInterpolator mInterpolatorY;
+ private final TimeInterpolator mInterpolatorZ;
+
+ public EpicenterTranslateClipReveal() {
+ mInterpolatorX = null;
+ mInterpolatorY = null;
+ mInterpolatorZ = null;
+ }
+
+ public EpicenterTranslateClipReveal(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.EpicenterTranslateClipReveal, 0, 0);
+
+ final int interpolatorX = a.getResourceId(
+ R.styleable.EpicenterTranslateClipReveal_interpolatorX, 0);
+ if (interpolatorX != 0) {
+ mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
+ } else {
+ mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN;
+ }
+
+ final int interpolatorY = a.getResourceId(
+ R.styleable.EpicenterTranslateClipReveal_interpolatorY, 0);
+ if (interpolatorY != 0) {
+ mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
+ } else {
+ mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
+ }
+
+ final int interpolatorZ = a.getResourceId(
+ R.styleable.EpicenterTranslateClipReveal_interpolatorZ, 0);
+ if (interpolatorZ != 0) {
+ mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ);
+ } else {
+ mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN;
+ }
+
+ a.recycle();
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ private void captureValues(TransitionValues values) {
+ final View view = values.view;
+ if (view.getVisibility() == View.GONE) {
+ return;
+ }
+
+ final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ values.values.put(PROPNAME_BOUNDS, bounds);
+ values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX());
+ values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY());
+ values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ());
+ values.values.put(PROPNAME_Z, view.getZ());
+
+ final Rect clip = view.getClipBounds();
+ values.values.put(PROPNAME_CLIP, clip);
+ }
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (endValues == null) {
+ return null;
+ }
+
+ final Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ final Rect startBounds = getEpicenterOrCenter(endBounds);
+ final float startX = startBounds.centerX() - endBounds.centerX();
+ final float startY = startBounds.centerY() - endBounds.centerY();
+ final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z);
+
+ // Translate the view to be centered on the epicenter.
+ view.setTranslationX(startX);
+ view.setTranslationY(startY);
+ view.setTranslationZ(startZ);
+
+ final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
+ final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
+ final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
+
+ final Rect endClip = getBestRect(endValues);
+ final Rect startClip = getEpicenterOrCenter(endClip);
+
+ // Prepare the view.
+ view.setClipBounds(startClip);
+
+ final State startStateX = new State(startClip.left, startClip.right, startX);
+ final State endStateX = new State(endClip.left, endClip.right, endX);
+ final State startStateY = new State(startClip.top, startClip.bottom, startY);
+ final State endStateY = new State(endClip.top, endClip.bottom, endY);
+
+ return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY,
+ endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ);
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (startValues == null) {
+ return null;
+ }
+
+ final Rect startBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ final Rect endBounds = getEpicenterOrCenter(startBounds);
+ final float endX = endBounds.centerX() - startBounds.centerX();
+ final float endY = endBounds.centerY() - startBounds.centerY();
+ final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z);
+
+ final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
+ final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
+ final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
+
+ final Rect startClip = getBestRect(startValues);
+ final Rect endClip = getEpicenterOrCenter(startClip);
+
+ // Prepare the view.
+ view.setClipBounds(startClip);
+
+ final State startStateX = new State(startClip.left, startClip.right, startX);
+ final State endStateX = new State(endClip.left, endClip.right, endX);
+ final State startStateY = new State(startClip.top, startClip.bottom, startY);
+ final State endStateY = new State(endClip.top, endClip.bottom, endY);
+
+ return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY,
+ endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ);
+ }
+
+ private Rect getEpicenterOrCenter(Rect bestRect) {
+ final Rect epicenter = getEpicenter();
+ if (epicenter != null) {
+ return epicenter;
+ }
+
+ final int centerX = bestRect.centerX();
+ final int centerY = bestRect.centerY();
+ return new Rect(centerX, centerY, centerX, centerY);
+ }
+
+ private Rect getBestRect(TransitionValues values) {
+ final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
+ if (clipRect == null) {
+ return (Rect) values.values.get(PROPNAME_BOUNDS);
+ }
+ return clipRect;
+ }
+
+ private static Animator createRectAnimator(final View view, State startX, State startY,
+ float startZ, State endX, State endY, float endZ, TransitionValues endValues,
+ TimeInterpolator interpolatorX, TimeInterpolator interpolatorY,
+ TimeInterpolator interpolatorZ) {
+ final StateEvaluator evaluator = new StateEvaluator();
+
+ final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ);
+ if (interpolatorZ != null) {
+ animZ.setInterpolator(interpolatorZ);
+ }
+
+ final StateProperty propX = new StateProperty(StateProperty.TARGET_X);
+ final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, startX, endX);
+ if (interpolatorX != null) {
+ animX.setInterpolator(interpolatorX);
+ }
+
+ final StateProperty propY = new StateProperty(StateProperty.TARGET_Y);
+ final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, startY, endY);
+ if (interpolatorY != null) {
+ animY.setInterpolator(interpolatorY);
+ }
+
+ final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);
+ final AnimatorListenerAdapter animatorListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setClipBounds(terminalClip);
+ }
+ };
+
+ final AnimatorSet animSet = new AnimatorSet();
+ animSet.playTogether(animX, animY, animZ);
+ animSet.addListener(animatorListener);
+ return animSet;
+ }
+
+ private static class State {
+ int lower;
+ int upper;
+ float trans;
+
+ public State() {}
+
+ public State(int lower, int upper, float trans) {
+ this.lower = lower;
+ this.upper = upper;
+ this.trans = trans;
+ }
+ }
+
+ private static class StateEvaluator implements TypeEvaluator<State> {
+ private final State mTemp = new State();
+
+ @Override
+ public State evaluate(float fraction, State startValue, State endValue) {
+ mTemp.upper = startValue.upper + (int) ((endValue.upper - startValue.upper) * fraction);
+ mTemp.lower = startValue.lower + (int) ((endValue.lower - startValue.lower) * fraction);
+ mTemp.trans = startValue.trans + (int) ((endValue.trans - startValue.trans) * fraction);
+ return mTemp;
+ }
+ }
+
+ private static class StateProperty extends Property<View, State> {
+ public static final char TARGET_X = 'x';
+ public static final char TARGET_Y = 'y';
+
+ private final Rect mTempRect = new Rect();
+ private final State mTempState = new State();
+
+ private final int mTargetDimension;
+
+ public StateProperty(char targetDimension) {
+ super(State.class, "state_" + targetDimension);
+
+ mTargetDimension = targetDimension;
+ }
+
+ @Override
+ public State get(View object) {
+ final Rect tempRect = mTempRect;
+ if (!object.getClipBounds(tempRect)) {
+ tempRect.setEmpty();
+ }
+ final State tempState = mTempState;
+ if (mTargetDimension == TARGET_X) {
+ tempState.trans = object.getTranslationX();
+ tempState.lower = tempRect.left + (int) tempState.trans;
+ tempState.upper = tempRect.right + (int) tempState.trans;
+ } else {
+ tempState.trans = object.getTranslationY();
+ tempState.lower = tempRect.top + (int) tempState.trans;
+ tempState.upper = tempRect.bottom + (int) tempState.trans;
+ }
+ return tempState;
+ }
+
+ @Override
+ public void set(View object, State value) {
+ final Rect tempRect = mTempRect;
+ if (object.getClipBounds(tempRect)) {
+ if (mTargetDimension == TARGET_X) {
+ tempRect.left = value.lower - (int) value.trans;
+ tempRect.right = value.upper - (int) value.trans;
+ } else {
+ tempRect.top = value.lower - (int) value.trans;
+ tempRect.bottom = value.upper - (int) value.trans;
+ }
+ object.setClipBounds(tempRect);
+ }
+
+ if (mTargetDimension == TARGET_X) {
+ object.setTranslationX(value.trans);
+ } else {
+ object.setTranslationY(value.trans);
+ }
+ }
+ }
+}
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 89c3749..4b544d2 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -16,7 +16,6 @@
-->
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -27,8 +26,7 @@
android:layout_column="0"
android:layout_row="0"
android:layout_rowSpan="3"
- android:layout_gravity="center|fill"
- tools:background="@color/accent_material_light" />
+ android:layout_gravity="center|fill" />
<RelativeLayout
android:layout_width="wrap_content"
@@ -56,20 +54,14 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="right"
- tools:text="23"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="right" />
<TextView
android:id="@+id/separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
- android:importantForAccessibility="no"
- tools:text=":"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:importantForAccessibility="no" />
<!-- The minutes should always be to the right of the separator,
regardless of the current locale's layout direction. -->
@@ -80,10 +72,7 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="left"
- tools:text="59"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="left" />
</LinearLayout>
<!-- The layout alignment of this view will switch between toRightOf
@@ -106,10 +95,7 @@
android:paddingTop="@dimen/timepicker_am_top_padding"
android:lines="1"
android:ellipsize="none"
- android:includeFontPadding="false"
- tools:text="AM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:includeFontPadding="false" />
<CheckedTextView
android:id="@+id/pm_label"
@@ -121,10 +107,7 @@
android:paddingTop="@dimen/timepicker_pm_top_padding"
android:lines="1"
android:ellipsize="none"
- android:includeFontPadding="false"
- tools:text="PM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:includeFontPadding="false" />
</LinearLayout>
</RelativeLayout>
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index 2150341..a4388f6 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -16,17 +16,13 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/date_picker_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="18dp"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingEnd="?attr/dialogPreferredPadding"
- android:orientation="vertical"
- tools:background="@color/accent_material_light"
- tools:paddingStart="24dp"
- tools:paddingEnd="24dp">
+ android:orientation="vertical">
<!-- Top padding should stay on this view so that
the touch target is a bit larger. -->
@@ -35,10 +31,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
- android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel"
- tools:text="2015"
- tools:textSize="@dimen/date_picker_year_label_size"
- tools:textColor="@color/white" />
+ android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel" />
<TextView
android:id="@+id/date_picker_header_date"
@@ -47,9 +40,6 @@
android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
android:gravity="start"
android:maxLines="2"
- android:ellipsize="none"
- tools:text="Thu, Sep 30"
- tools:textSize="@dimen/date_picker_date_label_size"
- tools:textColor="@color/white" />
+ android:ellipsize="none" />
</LinearLayout>
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index be9e443..3f5e300 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -18,13 +18,11 @@
<!-- This layout is duplicated in land/time_picker_material.xml, so any
changes made here need to be manually copied over. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/time_header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
- android:padding="@dimen/timepicker_separator_padding"
- tools:background="@color/accent_material_light">
+ android:padding="@dimen/timepicker_separator_padding">
<!-- The hour should always be to the left of the separator,
regardless of the current locale's layout direction. -->
@@ -37,10 +35,7 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="right"
- tools:text="23"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="right" />
<TextView
android:id="@+id/separator"
@@ -50,10 +45,7 @@
android:layout_marginRight="@dimen/timepicker_separator_padding"
android:layout_centerInParent="true"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
- android:importantForAccessibility="no"
- tools:text=":"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:importantForAccessibility="no" />
<!-- The minutes should always be to the left of the separator,
regardless of the current locale's layout direction. -->
@@ -66,10 +58,7 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="left"
- tools:text="59"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="left" />
<!-- The layout alignment of this view will switch between toRightOf
@id/minutes and toLeftOf @id/hours depending on the locale. -->
@@ -90,10 +79,7 @@
android:paddingTop="@dimen/timepicker_am_top_padding"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
- android:ellipsize="none"
- tools:text="AM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:ellipsize="none" />
<CheckedTextView
android:id="@+id/pm_label"
android:layout_width="wrap_content"
@@ -103,9 +89,6 @@
android:paddingTop="@dimen/timepicker_pm_top_padding"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
- android:ellipsize="none"
- tools:text="PM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:ellipsize="none" />
</LinearLayout>
</RelativeLayout>
diff --git a/core/res/res/transition/popup_window_enter.xml b/core/res/res/transition/popup_window_enter.xml
index 38c41f0..c4c8dac 100644
--- a/core/res/res/transition/popup_window_enter.xml
+++ b/core/res/res/transition/popup_window_enter.xml
@@ -16,17 +16,14 @@
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="together">
- <!-- Start from location of epicenter, move to popup location. -->
- <transition
- class="com.android.internal.transition.EpicenterTranslate"
- android:duration="300" />
-
<!-- Start from size of epicenter, expand to full width/height. -->
<transition
- class="com.android.internal.transition.EpicenterClipReveal"
- android:centerClipBounds="true"
- android:duration="300" />
+ class="com.android.internal.transition.EpicenterTranslateClipReveal"
+ android:duration="250" />
<!-- Quickly fade in. -->
- <fade android:duration="100" />
+ <fade
+ android:duration="100"
+ android:fromAlpha="0.1"
+ android:toAlpha="1.0" />
</transitionSet>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 1c4b5f7..eaa6278 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5877,16 +5877,9 @@
</declare-styleable>
<!-- @hide For internal use only. Use only as directed. -->
- <declare-styleable name="EpicenterClipReveal">
- <attr name="centerClipBounds" format="boolean" />
+ <declare-styleable name="EpicenterTranslateClipReveal">
<attr name="interpolatorX" format="reference" />
<attr name="interpolatorY" format="reference" />
- </declare-styleable>
-
- <!-- @hide For internal use only. Use only as directed. -->
- <declare-styleable name="EpicenterTranslate">
- <attr name="interpolatorX" />
- <attr name="interpolatorY" />
<attr name="interpolatorZ" format="reference" />
</declare-styleable>
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index 3b25ba6..3f29c6a 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -54,13 +54,13 @@
public static class RSA extends AndroidKeyPairGenerator {
public RSA() {
- super("RSA");
+ super(KeyStoreKeyProperties.Algorithm.RSA);
}
}
public static class EC extends AndroidKeyPairGenerator {
public EC() {
- super("EC");
+ super(KeyStoreKeyProperties.Algorithm.EC);
}
}
@@ -83,15 +83,15 @@
private android.security.KeyStore mKeyStore;
private KeyPairGeneratorSpec mSpec;
- private String mKeyAlgorithm;
+ private @KeyStoreKeyProperties.AlgorithmEnum String mKeyAlgorithm;
private int mKeyType;
private int mKeySize;
- protected AndroidKeyPairGenerator(String algorithm) {
+ protected AndroidKeyPairGenerator(@KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
mAlgorithm = algorithm;
}
- public String getAlgorithm() {
+ public @KeyStoreKeyProperties.AlgorithmEnum String getAlgorithm() {
return mAlgorithm;
}
@@ -197,7 +197,7 @@
return certGen.generate(privateKey);
}
- private String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
+ private @KeyStoreKeyProperties.AlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
String result = spec.getKeyType();
if (result != null) {
return result;
@@ -248,10 +248,11 @@
}
}
- private static String getDefaultSignatureAlgorithmForKeyAlgorithm(String algorithm) {
- if ("RSA".equalsIgnoreCase(algorithm)) {
+ private static String getDefaultSignatureAlgorithmForKeyAlgorithm(
+ @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
+ if (KeyStoreKeyProperties.Algorithm.RSA.equalsIgnoreCase(algorithm)) {
return "sha256WithRSA";
- } else if ("EC".equalsIgnoreCase(algorithm)) {
+ } else if (KeyStoreKeyProperties.Algorithm.EC.equalsIgnoreCase(algorithm)) {
return "sha256WithECDSA";
} else {
throw new IllegalArgumentException("Unsupported key type " + algorithm);
@@ -287,7 +288,7 @@
}
KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) params;
- String keyAlgorithm = getKeyAlgorithm(spec);
+ @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithm = getKeyAlgorithm(spec);
int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
if (keyType == -1) {
throw new InvalidAlgorithmParameterException(
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 72cb062..e82ff6a 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -128,10 +128,11 @@
keymasterDigest = keymasterDigests.get(0);
}
- String keyAlgorithmString;
+ @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithmString;
try {
- keyAlgorithmString = KeymasterUtils.getJcaSecretKeyAlgorithm(
- keymasterAlgorithm, keymasterDigest);
+ keyAlgorithmString =
+ KeyStoreKeyProperties.Algorithm.fromKeymasterSecretKeyAlgorithm(
+ keymasterAlgorithm, keymasterDigest);
} catch (IllegalArgumentException e) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
@@ -451,10 +452,10 @@
int keymasterAlgorithm;
int keymasterDigest;
try {
- keymasterAlgorithm = KeymasterUtils.getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(
+ keymasterAlgorithm = KeyStoreKeyProperties.Algorithm.toKeymasterSecretKeyAlgorithm(
keyAlgorithmString);
keymasterDigest =
- KeymasterUtils.getKeymasterDigestfromJcaSecretKeyAlgorithm(keyAlgorithmString);
+ KeyStoreKeyProperties.Algorithm.toKeymasterDigest(keyAlgorithmString);
} catch (IllegalArgumentException e) {
throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
}
@@ -465,8 +466,7 @@
int[] keymasterDigests;
if (params.isDigestsSpecified()) {
// Digest(s) specified in parameters
- keymasterDigests =
- KeymasterUtils.getKeymasterDigestsFromJcaDigestAlgorithms(params.getDigests());
+ keymasterDigests = KeyStoreKeyProperties.Digest.allToKeymaster(params.getDigests());
if (keymasterDigest != -1) {
// Digest also specified in the JCA key algorithm name.
if (!com.android.internal.util.ArrayUtils.contains(
@@ -494,8 +494,8 @@
}
@KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes();
- int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes(
- params.getBlockModes());
+ int[] keymasterBlockModes =
+ KeyStoreKeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
&& (params.isRandomizedEncryptionRequired())) {
for (int keymasterBlockMode : keymasterBlockModes) {
@@ -503,8 +503,7 @@
throw new KeyStoreException(
"Randomized encryption (IND-CPA) required but may be violated by block"
+ " mode: "
- + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode(
- keymasterBlockMode)
+ + KeyStoreKeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
+ ". See KeyStoreParameter documentation.");
}
}
@@ -513,11 +512,11 @@
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
}
args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
- int[] keymasterPaddings = ArrayUtils.concat(
- KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings(
- params.getEncryptionPaddings()),
- KeymasterUtils.getKeymasterPaddingsFromJcaSignaturePaddings(
- params.getSignaturePaddings()));
+ if (params.getSignaturePaddings().length > 0) {
+ throw new KeyStoreException("Signature paddings not supported for symmetric keys");
+ }
+ int[] keymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster(
+ params.getEncryptionPaddings());
args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
KeymasterUtils.addUserAuthArgs(args,
params.getContext(),
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index e9c24dd..8e27dc3 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -262,7 +262,8 @@
* unavailable.
*/
public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response,
- String[] keyTypes, Principal[] issuers, String host, int port, String alias) {
+ @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers,
+ String host, int port, String alias) {
choosePrivateKeyAlias(activity, response, keyTypes, issuers, host, port, null, alias);
}
@@ -306,9 +307,8 @@
* unavailable.
*/
public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response,
- String[] keyTypes, Principal[] issuers,
- String host, int port, String url,
- String alias) {
+ @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers,
+ String host, int port, String url, String alias) {
/*
* TODO currently keyTypes, issuers are unused. They are meant
* to follow the semantics and purpose of X509KeyManager
@@ -431,9 +431,11 @@
* specific {@code PrivateKey} type indicated by {@code algorithm} (e.g.,
* "RSA").
*/
- public static boolean isKeyAlgorithmSupported(String algorithm) {
+ public static boolean isKeyAlgorithmSupported(
+ @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
final String algUpper = algorithm.toUpperCase(Locale.US);
- return "EC".equals(algUpper) || "RSA".equals(algUpper);
+ return KeyStoreKeyProperties.Algorithm.EC.equals(algUpper)
+ || KeyStoreKeyProperties.Algorithm.RSA.equals(algUpper);
}
/**
@@ -443,7 +445,8 @@
* hardware support that can be used to bind keys to the device in a way
* that makes it non-exportable.
*/
- public static boolean isBoundKeyAlgorithm(String algorithm) {
+ public static boolean isBoundKeyAlgorithm(
+ @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
if (!isKeyAlgorithmSupported(algorithm)) {
return false;
}
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
index 8f135a6..729646d 100644
--- a/keystore/java/android/security/KeyGeneratorSpec.java
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -48,8 +48,8 @@
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final String[] mEncryptionPaddings;
- private final String[] mBlockModes;
+ private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
@@ -63,8 +63,8 @@
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] encryptionPaddings,
- String[] blockModes,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
@@ -160,14 +160,14 @@
/**
* Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -195,10 +195,12 @@
/**
* Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated.
+ * successfully authenticated. This has effect only if user authentication is required.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
+ *
+ * @see #isUserAuthenticationRequired()
*/
public int getUserAuthenticationValidityDurationSeconds() {
return mUserAuthenticationValidityDurationSeconds;
@@ -220,8 +222,8 @@
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private String[] mEncryptionPaddings;
- private String[] mBlockModes;
+ private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
private int mUserAuthenticationValidityDurationSeconds = -1;
@@ -346,7 +348,8 @@
*
* <p>This must be specified for keys which are used for encryption/decryption.
*/
- public Builder setEncryptionPaddings(String... paddings) {
+ public Builder setEncryptionPaddings(
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -357,7 +360,7 @@
*
* <p>This must be specified for encryption/decryption keys.
*/
- public Builder setBlockModes(String... blockModes) {
+ public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
return this;
}
@@ -425,7 +428,7 @@
*
* <p>By default, the user needs to authenticate for every use of the key.
*
- * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
* every use of the key.
*
* @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index d6d3789..25c61fd 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -85,13 +85,13 @@
private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final String[] mDigests;
+ private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
- private final String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private final String[] mSignaturePaddings;
+ private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
- private final String[] mBlockModes;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
@@ -138,10 +138,10 @@
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] digests,
- String[] encryptionPaddings,
- String[] signaturePaddings,
- String[] blockModes,
+ @KeyStoreKeyProperties.DigestEnum String[] digests,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
@@ -246,7 +246,7 @@
/**
* Returns the key type (e.g., "EC", "RSA") specified by this parameter.
*/
- public String getKeyType() {
+ public @KeyStoreKeyProperties.AlgorithmEnum String getKeyType() {
return mKeyType;
}
@@ -352,28 +352,28 @@
/**
* Gets the set of digest algorithms with which the key can be used.
*/
- public String[] getDigests() {
+ public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
return ArrayUtils.cloneIfNotEmpty(mDigests);
}
/**
* Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
/**
* Gets the set of padding schemes with which the key can be used when signing/verifying.
*/
- public String[] getSignaturePaddings() {
+ public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
}
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -403,14 +403,14 @@
}
/**
- * Gets the duration of time (seconds) for which the private key can be used after the user
- * is successfully authenticated.
+ * Gets the duration of time (seconds) for which this key can be used after the user is
+ * successfully authenticated. This has effect only if user authentication is required.
*
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
*
* @see #isUserAuthenticationRequired()
*/
@@ -468,13 +468,13 @@
private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private String[] mDigests;
+ private @KeyStoreKeyProperties.DigestEnum String[] mDigests;
- private String[] mEncryptionPaddings;
+ private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private String[] mSignaturePaddings;
+ private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
- private String[] mBlockModes;
+ private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
@@ -511,7 +511,8 @@
/**
* Sets the key type (e.g., EC, RSA) of the keypair to be created.
*/
- public Builder setKeyType(String keyType) throws NoSuchAlgorithmException {
+ public Builder setKeyType(@KeyStoreKeyProperties.AlgorithmEnum String keyType)
+ throws NoSuchAlgorithmException {
if (keyType == null) {
throw new NullPointerException("keyType == null");
} else {
@@ -628,6 +629,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect.
+ *
* @see #setKeyValidityEnd(Date)
*/
public Builder setKeyValidityStart(Date startDate) {
@@ -640,6 +643,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect.
+ *
* @see #setKeyValidityStart(Date)
* @see #setKeyValidityForConsumptionEnd(Date)
* @see #setKeyValidityForOriginationEnd(Date)
@@ -655,6 +660,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect.
+ *
* @see #setKeyValidityForConsumptionEnd(Date)
*/
public Builder setKeyValidityForOriginationEnd(Date endDate) {
@@ -668,6 +675,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect.
+ *
* @see #setKeyValidityForOriginationEnd(Date)
*/
public Builder setKeyValidityForConsumptionEnd(Date endDate) {
@@ -679,6 +688,8 @@
* Sets the set of purposes for which the key can be used.
*
* <p>This must be specified for all keys. There is no default.
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
mPurposes = purposes;
@@ -690,8 +701,10 @@
* to use the key with any other digest will be rejected.
*
* <p>This must be specified for keys which are used for signing/verification.
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
- public Builder setDigests(String... digests) {
+ public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
return this;
}
@@ -702,8 +715,11 @@
* rejected.
*
* <p>This must be specified for keys which are used for encryption/decryption.
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
- public Builder setEncryptionPaddings(String... paddings) {
+ public Builder setEncryptionPaddings(
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -714,8 +730,11 @@
* rejected.
*
* <p>This must be specified for RSA keys which are used for signing/verification.
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
- public Builder setSignaturePaddings(String... paddings) {
+ public Builder setSignaturePaddings(
+ @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -725,8 +744,10 @@
* Attempts to use the key with any other block modes will be rejected.
*
* <p>This must be specified for encryption/decryption keys.
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
- public Builder setBlockModes(String... blockModes) {
+ public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
return this;
}
@@ -750,6 +771,8 @@
* <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>
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
public Builder setRandomizedEncryptionRequired(boolean required) {
mRandomizedEncryptionRequired = required;
@@ -772,6 +795,8 @@
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
+ * <p><b>NOTE: This has currently no effect.
+ *
* @see #setUserAuthenticationValidityDurationSeconds(int)
*/
public Builder setUserAuthenticationRequired(boolean required) {
@@ -788,7 +813,9 @@
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
- * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * <p><b>NOTE: This has currently no effect.
+ *
+ * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
* every use of the key.
*
* @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 82d328b..304d277 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -115,10 +115,10 @@
return mToken;
}
- static int getKeyTypeForAlgorithm(String keyType) {
- if ("RSA".equalsIgnoreCase(keyType)) {
+ static int getKeyTypeForAlgorithm(@KeyStoreKeyProperties.AlgorithmEnum String keyType) {
+ if (KeyStoreKeyProperties.Algorithm.RSA.equalsIgnoreCase(keyType)) {
return NativeConstants.EVP_PKEY_RSA;
- } else if ("EC".equalsIgnoreCase(keyType)) {
+ } else if (KeyStoreKeyProperties.Algorithm.EC.equalsIgnoreCase(keyType)) {
return NativeConstants.EVP_PKEY_EC;
} else {
return -1;
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index 094aa75..bd601bc 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -27,6 +27,7 @@
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
@@ -315,15 +316,15 @@
} else if (e instanceof InvalidAlgorithmParameterException) {
throw (InvalidAlgorithmParameterException) e;
} else {
- throw new RuntimeException("Unexpected exception type", e);
+ throw new ProviderException("Unexpected exception type", e);
}
}
if (mOperationToken == null) {
- throw new IllegalStateException("Keystore returned null operation token");
+ throw new ProviderException("Keystore returned null operation token");
}
if (mOperationHandle == 0) {
- throw new IllegalStateException("Keystore returned invalid operation handle");
+ throw new ProviderException("Keystore returned invalid operation handle");
}
loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs);
@@ -494,13 +495,14 @@
}
if ((mIv != null) && (mIv.length > 0)) {
try {
- AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+ AlgorithmParameters params =
+ AlgorithmParameters.getInstance(KeyStoreKeyProperties.Algorithm.AES);
params.init(new IvParameterSpec(mIv));
return params;
} catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("Failed to obtain AES AlgorithmParameters", e);
+ throw new ProviderException("Failed to obtain AES AlgorithmParameters", e);
} catch (InvalidParameterSpecException e) {
- throw new RuntimeException(
+ throw new ProviderException(
"Failed to initialize AES AlgorithmParameters with an IV", e);
}
}
@@ -633,10 +635,9 @@
if ((mIv == null) && (mEncrypting)) {
// IV was not provided by the caller and thus will be generated by keymaster.
// Mix in some additional entropy from the provided SecureRandom.
- if (mRng != null) {
- mAdditionalEntropyForBegin = new byte[mBlockSizeBytes];
- mRng.nextBytes(mAdditionalEntropyForBegin);
- }
+ mAdditionalEntropyForBegin =
+ KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ mRng, mBlockSizeBytes);
}
}
}
@@ -668,11 +669,11 @@
if (mIv == null) {
mIv = returnedIv;
} else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
- throw new IllegalStateException("IV in use differs from provided IV");
+ throw new ProviderException("IV in use differs from provided IV");
}
} else {
if (returnedIv != null) {
- throw new IllegalStateException(
+ throw new ProviderException(
"IV in use despite IV not being used by this transformation");
}
}
diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java
index 1aa3aec..885f1f7 100644
--- a/keystore/java/android/security/KeyStoreConnectException.java
+++ b/keystore/java/android/security/KeyStoreConnectException.java
@@ -16,12 +16,14 @@
package android.security;
+import java.security.ProviderException;
+
/**
* Indicates a communications error with keystore service.
*
* @hide
*/
-public class KeyStoreConnectException extends IllegalStateException {
+public class KeyStoreConnectException extends ProviderException {
public KeyStoreConnectException() {
super("Failed to communicate with keystore service");
}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
index e5933ad..311278b 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
@@ -21,6 +21,7 @@
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
+import java.security.SecureRandom;
/**
* Assorted utility methods for implementing crypto operations on top of KeyStore.
@@ -28,6 +29,9 @@
* @hide
*/
abstract class KeyStoreCryptoOperationUtils {
+
+ private static volatile SecureRandom sRng;
+
private KeyStoreCryptoOperationUtils() {}
/**
@@ -81,4 +85,28 @@
// General cases
return getInvalidKeyExceptionForInit(keyStore, key, beginOpResultCode);
}
+
+ /**
+ * Returns the requested number of random bytes to mix into keystore/keymaster RNG.
+ *
+ * @param rng RNG from which to obtain the random bytes or {@code null} for the platform-default
+ * RNG.
+ */
+ static byte[] getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes) {
+ if (rng == null) {
+ rng = getRng();
+ }
+ byte[] result = new byte[sizeBytes];
+ rng.nextBytes(result);
+ return result;
+ }
+
+ private static SecureRandom getRng() {
+ // IMPLEMENTATION NOTE: It's OK to share a SecureRandom instance because SecureRandom is
+ // required to be thread-safe.
+ if (sRng == null) {
+ sRng = new SecureRandom();
+ }
+ return sRng;
+ }
}
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index 0dbe788..5089a25 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -24,6 +24,7 @@
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
+import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.MacSpi;
@@ -185,10 +186,10 @@
}
if (mOperationToken == null) {
- throw new IllegalStateException("Keystore returned null operation token");
+ throw new ProviderException("Keystore returned null operation token");
}
if (mOperationHandle == 0) {
- throw new IllegalStateException("Keystore returned invalid operation handle");
+ throw new ProviderException("Keystore returned invalid operation handle");
}
mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
@@ -206,17 +207,17 @@
try {
ensureKeystoreOperationInitialized();
} catch (InvalidKeyException e) {
- throw new IllegalStateException("Failed to reinitialize MAC", e);
+ throw new ProviderException("Failed to reinitialize MAC", e);
}
byte[] output;
try {
output = mChunkedStreamer.update(input, offset, len);
} catch (KeyStoreException e) {
- throw new IllegalStateException("Keystore operation failed", e);
+ throw new ProviderException("Keystore operation failed", e);
}
if ((output != null) && (output.length != 0)) {
- throw new IllegalStateException("Update operation unexpectedly produced output");
+ throw new ProviderException("Update operation unexpectedly produced output");
}
}
@@ -225,14 +226,14 @@
try {
ensureKeystoreOperationInitialized();
} catch (InvalidKeyException e) {
- throw new IllegalStateException("Failed to reinitialize MAC", e);
+ throw new ProviderException("Failed to reinitialize MAC", e);
}
byte[] result;
try {
result = mChunkedStreamer.doFinal(null, 0, 0);
} catch (KeyStoreException e) {
- throw new IllegalStateException("Keystore operation failed", e);
+ throw new ProviderException("Keystore operation failed", e);
}
resetWhilePreservingInitState();
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 68b5751..4b914c2 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -21,6 +21,7 @@
import android.security.keymaster.KeymasterDefs;
import java.security.InvalidAlgorithmParameterException;
+import java.security.ProviderException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Date;
@@ -39,6 +40,17 @@
public AES() {
super(KeymasterDefs.KM_ALGORITHM_AES, 128);
}
+
+ @Override
+ protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidAlgorithmParameterException {
+ super.engineInit(params, random);
+ if ((mKeySizeBits != 128) && (mKeySizeBits != 192) && (mKeySizeBits != 256)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported key size: " + mKeySizeBits
+ + ". Supported: 128, 192, 256.");
+ }
+ }
}
protected static abstract class HmacBase extends KeyStoreKeyGeneratorSpi {
@@ -87,6 +99,11 @@
private KeyGeneratorSpec mSpec;
private SecureRandom mRng;
+ protected int mKeySizeBits;
+ private int[] mKeymasterPurposes;
+ private int[] mKeymasterBlockModes;
+ private int[] mKeymasterPaddings;
+
protected KeyStoreKeyGeneratorSpi(
int keymasterAlgorithm,
int defaultKeySizeBits) {
@@ -100,6 +117,97 @@
mKeymasterAlgorithm = keymasterAlgorithm;
mKeymasterDigest = keymasterDigest;
mDefaultKeySizeBits = defaultKeySizeBits;
+ if (mDefaultKeySizeBits <= 0) {
+ throw new IllegalArgumentException("Default key size must be positive");
+ }
+
+ if ((mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) && (mKeymasterDigest == -1)) {
+ throw new IllegalArgumentException(
+ "Digest algorithm must be specified for HMAC key");
+ }
+ }
+
+ @Override
+ protected void engineInit(SecureRandom random) {
+ throw new UnsupportedOperationException("Cannot initialize without an "
+ + KeyGeneratorSpec.class.getName() + " parameter");
+ }
+
+ @Override
+ protected void engineInit(int keySize, SecureRandom random) {
+ throw new UnsupportedOperationException("Cannot initialize without a "
+ + KeyGeneratorSpec.class.getName() + " parameter");
+ }
+
+ @Override
+ protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidAlgorithmParameterException {
+ resetAll();
+
+ boolean success = false;
+ try {
+ if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
+ throw new InvalidAlgorithmParameterException("Cannot initialize without an "
+ + KeyGeneratorSpec.class.getName() + " parameter");
+ }
+ KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
+ if (spec.getKeystoreAlias() == null) {
+ throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+ }
+
+ mRng = random;
+ mSpec = spec;
+
+ mKeySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
+ if (mKeySizeBits <= 0) {
+ throw new InvalidAlgorithmParameterException(
+ "Key size must be positive: " + mKeySizeBits);
+ } else if ((mKeySizeBits % 8) != 0) {
+ throw new InvalidAlgorithmParameterException(
+ "Key size in must be a multiple of 8: " + mKeySizeBits);
+ }
+
+ try {
+ mKeymasterPurposes =
+ KeyStoreKeyProperties.Purpose.allToKeymaster(spec.getPurposes());
+ mKeymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster(
+ spec.getEncryptionPaddings());
+ mKeymasterBlockModes =
+ KeyStoreKeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
+ if (((spec.getPurposes() & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
+ && (spec.isRandomizedEncryptionRequired())) {
+ for (int keymasterBlockMode : mKeymasterBlockModes) {
+ if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(
+ keymasterBlockMode)) {
+ throw new InvalidAlgorithmParameterException(
+ "Randomized encryption (IND-CPA) required but may be violated"
+ + " by block mode: "
+ + KeyStoreKeyProperties.BlockMode.fromKeymaster(
+ keymasterBlockMode)
+ + ". See " + KeyGeneratorSpec.class.getName()
+ + " documentation.");
+ }
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(e);
+ }
+
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ private void resetAll() {
+ mSpec = null;
+ mRng = null;
+ mKeySizeBits = -1;
+ mKeymasterPurposes = null;
+ mKeymasterPaddings = null;
+ mKeymasterBlockModes = null;
}
@Override
@@ -117,43 +225,14 @@
}
KeymasterArguments args = new KeymasterArguments();
+ args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
if (mKeymasterDigest != -1) {
args.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
}
- if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
- if (mKeymasterDigest == -1) {
- throw new IllegalStateException("Digest algorithm must be specified for HMAC key");
- }
- }
- int keySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
- args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
- @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 :
- KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) {
- args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
- }
- args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
- args.addInts(
- KeymasterDefs.KM_TAG_PADDING,
- KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings(
- spec.getEncryptionPaddings()));
+ args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
+ args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
+ args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
KeymasterUtils.addUserAuthArgs(args,
spec.getContext(),
spec.isUserAuthenticationRequired(),
@@ -168,57 +247,31 @@
(spec.getKeyValidityForConsumptionEnd() != null)
? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
- if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
+ if (((spec.getPurposes() & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
&& (!spec.isRandomizedEncryptionRequired())) {
// Permit caller-provided IV when encrypting with this key
args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
}
- byte[] additionalEntropy = null;
- SecureRandom rng = mRng;
- if (rng != null) {
- additionalEntropy = new byte[(keySizeBits + 7) / 8];
- rng.nextBytes(additionalEntropy);
- }
-
+ byte[] additionalEntropy =
+ KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ mRng, (mKeySizeBits + 7) / 8);
int flags = spec.getFlags();
String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
+ KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
int errorCode = mKeyStore.generateKey(
- keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
+ keyAliasInKeystore, args, additionalEntropy, flags, resultingKeyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
- throw new IllegalStateException(
+ throw new ProviderException(
"Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
}
- String keyAlgorithmJCA =
- KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest);
+ String keyAlgorithmJCA;
+ try {
+ keyAlgorithmJCA = KeyStoreKeyProperties.Algorithm.fromKeymasterSecretKeyAlgorithm(
+ mKeymasterAlgorithm, mKeymasterDigest);
+ } catch (IllegalArgumentException e) {
+ throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
+ }
return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
}
-
- @Override
- protected void engineInit(SecureRandom random) {
- throw new UnsupportedOperationException("Cannot initialize without an "
- + KeyGeneratorSpec.class.getName() + " parameter");
- }
-
- @Override
- protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
- throws InvalidAlgorithmParameterException {
- if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
- throw new InvalidAlgorithmParameterException("Cannot initialize without an "
- + KeyGeneratorSpec.class.getName() + " parameter");
- }
- KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
- if (spec.getKeystoreAlias() == null) {
- throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
- }
-
- mSpec = spec;
- mRng = random;
- }
-
- @Override
- protected void engineInit(int keySize, SecureRandom random) {
- throw new UnsupportedOperationException("Cannot initialize without a "
- + KeyGeneratorSpec.class.getName() + " parameter");
- }
}
diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java
index b85ec53..1c3e300 100644
--- a/keystore/java/android/security/KeyStoreKeyProperties.java
+++ b/keystore/java/android/security/KeyStoreKeyProperties.java
@@ -17,13 +17,23 @@
package android.security;
import android.annotation.IntDef;
+import android.annotation.StringDef;
import android.security.keymaster.KeymasterDefs;
import libcore.util.EmptyArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
import java.util.Collection;
+import java.util.Locale;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKeyFactory;
/**
* Properties of {@code AndroidKeyStore} keys.
@@ -37,7 +47,7 @@
public @interface PurposeEnum {}
/**
- * Purpose of key.
+ * Purposes of key.
*/
public static abstract class Purpose {
private Purpose() {}
@@ -122,6 +132,514 @@
}
@Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ Algorithm.RSA,
+ Algorithm.EC,
+ Algorithm.AES,
+ Algorithm.HMAC_SHA1,
+ Algorithm.HMAC_SHA224,
+ Algorithm.HMAC_SHA256,
+ Algorithm.HMAC_SHA384,
+ Algorithm.HMAC_SHA512,
+ })
+ public @interface AlgorithmEnum {}
+
+ /**
+ * Key algorithms.
+ *
+ * <p>These are standard names which can be used to obtain instances of {@link KeyGenerator},
+ * {@link KeyPairGenerator}, {@link Cipher} (as part of the transformation string), {@link Mac},
+ * {@link KeyFactory}, {@link SecretKeyFactory}. These are also the names used by
+ * {@link Key#getAlgorithm()}.
+ */
+ public static abstract class Algorithm {
+ private Algorithm() {}
+
+ /** Rivest Shamir Adleman (RSA) key. */
+ public static final String RSA = "RSA";
+
+ /** Elliptic Curve (EC) key. */
+ public static final String EC = "EC";
+
+ /** Advanced Encryption Standard (AES) key. */
+ public static final String AES = "AES";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
+ public static final String HMAC_SHA1 = "HmacSHA1";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */
+ public static final String HMAC_SHA224 = "HmacSHA224";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */
+ public static final String HMAC_SHA256 = "HmacSHA256";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */
+ public static final String HMAC_SHA384 = "HmacSHA384";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
+ public static final String HMAC_SHA512 = "HmacSHA512";
+
+ /**
+ * @hide
+ */
+ static int toKeymasterSecretKeyAlgorithm(@AlgorithmEnum String algorithm) {
+ if (AES.equalsIgnoreCase(algorithm)) {
+ return KeymasterDefs.KM_ALGORITHM_AES;
+ } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
+ return KeymasterDefs.KM_ALGORITHM_HMAC;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported secret key algorithm: " + algorithm);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @AlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
+ int keymasterAlgorithm, int keymasterDigest) {
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_AES:
+ if (keymasterDigest != -1) {
+ throw new IllegalArgumentException("Digest not supported for AES key: "
+ + Digest.fromKeymaster(keymasterDigest));
+ }
+ return AES;
+ case KeymasterDefs.KM_ALGORITHM_HMAC:
+ switch (keymasterDigest) {
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return HMAC_SHA1;
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return HMAC_SHA224;
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return HMAC_SHA256;
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return HMAC_SHA384;
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return HMAC_SHA512;
+ default:
+ throw new IllegalArgumentException("Unsupported HMAC digest: "
+ + Digest.fromKeymaster(keymasterDigest));
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported algorithm: " + keymasterAlgorithm);
+ }
+ }
+
+ /**
+ * @hide
+ *
+ * @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
+ */
+ static int toKeymasterDigest(@AlgorithmEnum String algorithm) {
+ String algorithmUpper = algorithm.toUpperCase(Locale.US);
+ if (algorithmUpper.startsWith("HMAC")) {
+ String digestUpper = algorithmUpper.substring("HMAC".length());
+ switch (digestUpper) {
+ 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;
+ }
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ BlockMode.ECB,
+ BlockMode.CBC,
+ BlockMode.CTR,
+ BlockMode.GCM,
+ })
+ 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 String ECB = "ECB";
+
+ /** Cipher Block Chaining (CBC) block mode. */
+ public static final String CBC = "CBC";
+
+ /** Counter (CTR) block mode. */
+ public static final String CTR = "CTR";
+
+ /** Galois/Counter Mode (GCM) block mode. */
+ public static final String GCM = "GCM";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@BlockModeEnum String blockMode) {
+ if (ECB.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_ECB;
+ } else if (CBC.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_CBC;
+ } else if (CTR.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_CTR;
+ } else if (GCM.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_GCM;
+ } else {
+ throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @BlockModeEnum String fromKeymaster(int blockMode) {
+ switch (blockMode) {
+ 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: " + blockMode);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @BlockModeEnum String[] allFromKeymaster(Collection<Integer> blockModes) {
+ if ((blockModes == null) || (blockModes.isEmpty())) {
+ return EmptyArray.STRING;
+ }
+ @BlockModeEnum String[] result = new String[blockModes.size()];
+ int offset = 0;
+ for (int blockMode : blockModes) {
+ result[offset] = fromKeymaster(blockMode);
+ offset++;
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@BlockModeEnum String[] blockModes) {
+ if ((blockModes == null) || (blockModes.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[blockModes.length];
+ for (int i = 0; i < blockModes.length; i++) {
+ result[i] = toKeymaster(blockModes[i]);
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ EncryptionPadding.NONE,
+ EncryptionPadding.PKCS7,
+ EncryptionPadding.RSA_PKCS1,
+ EncryptionPadding.RSA_OAEP,
+ })
+ public @interface EncryptionPaddingEnum {}
+
+ /**
+ * Padding schemes for encryption/decryption.
+ */
+ public static abstract class EncryptionPadding {
+ private EncryptionPadding() {}
+
+ /**
+ * No padding.
+ */
+ public static final String NONE = "NoPadding";
+
+ /**
+ * PKCS#7 padding.
+ */
+ public static final String PKCS7 = "PKCS7Padding";
+
+ /**
+ * RSA PKCS#1 v1.5 padding for encryption/decryption.
+ */
+ public static final String RSA_PKCS1 = "PKCS1Padding";
+
+ /**
+ * RSA Optimal Asymmetric Encryption Padding (OAEP).
+ */
+ public static final String RSA_OAEP = "OAEPPadding";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@EncryptionPaddingEnum String padding) {
+ if (NONE.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_NONE;
+ } else if (PKCS7.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_PKCS7;
+ } else if (RSA_PKCS1.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
+ } else if (RSA_OAEP.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_RSA_OAEP;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported encryption padding scheme: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @EncryptionPaddingEnum String fromKeymaster(int padding) {
+ switch (padding) {
+ case KeymasterDefs.KM_PAD_NONE:
+ return NONE;
+ case KeymasterDefs.KM_PAD_PKCS7:
+ return PKCS7;
+ case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+ return RSA_PKCS1;
+ case KeymasterDefs.KM_PAD_RSA_OAEP:
+ return RSA_OAEP;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported encryption padding: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@EncryptionPaddingEnum String[] paddings) {
+ if ((paddings == null) || (paddings.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[paddings.length];
+ for (int i = 0; i < paddings.length; i++) {
+ result[i] = toKeymaster(paddings[i]);
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ SignaturePadding.RSA_PKCS1,
+ SignaturePadding.RSA_PSS,
+ })
+ public @interface SignaturePaddingEnum {}
+
+ /**
+ * Padding schemes for signing/verification.
+ */
+ public static abstract class SignaturePadding {
+ private SignaturePadding() {}
+
+ /**
+ * RSA PKCS#1 v1.5 padding for signatures.
+ */
+ public static final String RSA_PKCS1 = "PKCS1";
+
+ /**
+ * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
+ */
+ public static final String RSA_PSS = "PSS";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@SignaturePaddingEnum String padding) {
+ switch (padding.toUpperCase(Locale.US)) {
+ case RSA_PKCS1:
+ return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
+ case RSA_PSS:
+ return KeymasterDefs.KM_PAD_RSA_PSS;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported signature padding scheme: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @SignaturePaddingEnum String fromKeymaster(int padding) {
+ switch (padding) {
+ case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
+ return RSA_PKCS1;
+ case KeymasterDefs.KM_PAD_RSA_PSS:
+ return RSA_PSS;
+ default:
+ throw new IllegalArgumentException("Unsupported signature padding: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@SignaturePaddingEnum String[] paddings) {
+ if ((paddings == null) || (paddings.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[paddings.length];
+ for (int i = 0; i < paddings.length; i++) {
+ result[i] = toKeymaster(paddings[i]);
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ Digest.NONE,
+ Digest.MD5,
+ Digest.SHA1,
+ Digest.SHA224,
+ Digest.SHA256,
+ Digest.SHA384,
+ Digest.SHA512,
+ })
+ 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 String NONE = "NONE";
+
+ /**
+ * MD5 digest.
+ */
+ public static final String MD5 = "MD5";
+
+ /**
+ * SHA-1 digest.
+ */
+ public static final String SHA1 = "SHA-1";
+
+ /**
+ * SHA-2 224 (aka SHA-224) digest.
+ */
+ public static final String SHA224 = "SHA-224";
+
+ /**
+ * SHA-2 256 (aka SHA-256) digest.
+ */
+ public static final String SHA256 = "SHA-256";
+
+ /**
+ * SHA-2 384 (aka SHA-384) digest.
+ */
+ public static final String SHA384 = "SHA-384";
+
+ /**
+ * SHA-2 512 (aka SHA-512) digest.
+ */
+ public static final String SHA512 = "SHA-512";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@DigestEnum String digest) {
+ switch (digest.toUpperCase(Locale.US)) {
+ 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;
+ case NONE:
+ return KeymasterDefs.KM_DIGEST_NONE;
+ case MD5:
+ return KeymasterDefs.KM_DIGEST_MD5;
+ default:
+ throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @DigestEnum String fromKeymaster(int digest) {
+ switch (digest) {
+ case KeymasterDefs.KM_DIGEST_NONE:
+ return NONE;
+ case KeymasterDefs.KM_DIGEST_MD5:
+ return MD5;
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return SHA1;
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return SHA224;
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return SHA256;
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return SHA384;
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return SHA512;
+ default:
+ throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @DigestEnum String[] allFromKeymaster(Collection<Integer> digests) {
+ if (digests.isEmpty()) {
+ return EmptyArray.STRING;
+ }
+ String[] result = new String[digests.size()];
+ int offset = 0;
+ for (int digest : digests) {
+ result[offset] = fromKeymaster(digest);
+ offset++;
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@DigestEnum String[] digests) {
+ if ((digests == null) || (digests.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[digests.length];
+ int offset = 0;
+ for (@DigestEnum String digest : digests) {
+ result[offset] = toKeymaster(digest);
+ offset++;
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
@IntDef({Origin.GENERATED, Origin.IMPORTED, Origin.UNKNOWN})
public @interface OriginEnum {}
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
index 96d58d8..7533bdc 100644
--- a/keystore/java/android/security/KeyStoreKeySpec.java
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -32,10 +32,10 @@
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
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.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
private final boolean mUserAuthenticationRequirementTeeEnforced;
@@ -51,10 +51,10 @@
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] encryptionPaddings,
- String[] signaturePaddings,
- String[] digests,
- String[] blockModes,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyStoreKeyProperties.DigestEnum String[] digests,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds,
boolean userAuthenticationRequirementTeeEnforced) {
@@ -143,28 +143,28 @@
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
/**
* Gets the set of padding modes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
/**
* Gets the set of padding modes with which the key can be used when signing/verifying.
*/
- public String[] getSignaturePaddings() {
+ public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
}
/**
* Gets the set of digest algorithms with which the key can be used.
*/
- public String[] getDigests() {
+ public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
return ArrayUtils.cloneIfNotEmpty(mDigests);
}
@@ -179,10 +179,10 @@
/**
* Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated.
+ * successfully authenticated. This has effect only if user authentication is required.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
*
* @see #isUserAuthenticationRequired()
*/
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index b4747e9..8d7a19f 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -45,10 +45,10 @@
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
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.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
@@ -60,10 +60,10 @@
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] encryptionPaddings,
- String[] signaturePaddings,
- String[] digests,
- String[] blockModes,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyStoreKeyProperties.DigestEnum String[] digests,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
@@ -151,7 +151,7 @@
/**
* Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
@@ -159,7 +159,7 @@
* Gets the set of padding schemes with which the key can be used when signing or verifying
* signatures.
*/
- public String[] getSignaturePaddings() {
+ public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
}
@@ -170,7 +170,7 @@
*
* @see #isDigestsSpecified()
*/
- public String[] getDigests() {
+ public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
if (mDigests == null) {
throw new IllegalStateException("Digests not specified");
}
@@ -190,7 +190,7 @@
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -218,10 +218,12 @@
/**
* Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated.
+ * successfully authenticated. This has effect only if user authentication is required.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
+ *
+ * @see #isUserAuthenticationRequired()
*/
public int getUserAuthenticationValidityDurationSeconds() {
return mUserAuthenticationValidityDurationSeconds;
@@ -251,10 +253,10 @@
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private String[] mEncryptionPaddings;
- private String[] mSignaturePaddings;
- private String[] mDigests;
- private String[] mBlockModes;
+ private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+ private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
private int mUserAuthenticationValidityDurationSeconds = -1;
@@ -292,6 +294,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
* @see #setKeyValidityEnd(Date)
*/
public Builder setKeyValidityStart(Date startDate) {
@@ -304,6 +308,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
* @see #setKeyValidityStart(Date)
* @see #setKeyValidityForConsumptionEnd(Date)
* @see #setKeyValidityForOriginationEnd(Date)
@@ -319,6 +325,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
* @see #setKeyValidityForConsumptionEnd(Date)
*/
public Builder setKeyValidityForOriginationEnd(Date endDate) {
@@ -332,6 +340,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
* @see #setKeyValidityForOriginationEnd(Date)
*/
public Builder setKeyValidityForConsumptionEnd(Date endDate) {
@@ -343,6 +353,8 @@
* Sets the set of purposes for which the key can be used.
*
* <p>This must be specified for all keys. There is no default.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
mPurposes = purposes;
@@ -355,8 +367,11 @@
* rejected.
*
* <p>This must be specified for keys which are used for encryption/decryption.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
- public Builder setEncryptionPaddings(String... paddings) {
+ public Builder setEncryptionPaddings(
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -367,8 +382,11 @@
* rejected.
*
* <p>This must be specified for RSA keys which are used for signing/verification.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
- public Builder setSignaturePaddings(String... paddings) {
+ public Builder setSignaturePaddings(
+ @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -380,8 +398,10 @@
*
* <p>For HMAC keys, the default is the digest specified in {@link Key#getAlgorithm()}. For
* asymmetric signing keys this constraint must be specified.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
- public Builder setDigests(String... digests) {
+ public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
return this;
}
@@ -391,8 +411,10 @@
* Attempts to use the key with any other block modes will be rejected.
*
* <p>This must be specified for encryption/decryption keys.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
- public Builder setBlockModes(String... blockModes) {
+ public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
return this;
}
@@ -430,6 +452,8 @@
* <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>
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
public Builder setRandomizedEncryptionRequired(boolean required) {
mRandomizedEncryptionRequired = required;
@@ -449,6 +473,8 @@
* <a href="{@docRoot}training/articles/keystore.html#UserAuthentication">More
* information</a>.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
* @see #setUserAuthenticationValidityDurationSeconds(int)
*/
public Builder setUserAuthenticationRequired(boolean required) {
@@ -462,7 +488,9 @@
*
* <p>By default, the user needs to authenticate for every use of the key.
*
- * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
+ * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
* every use of the key.
*
* @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
index bfe09e3..ff79b7a 100644
--- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -79,8 +79,8 @@
int keySize;
@KeyStoreKeyProperties.PurposeEnum int purposes;
String[] encryptionPaddings;
- String[] digests;
- String[] blockModes;
+ @KeyStoreKeyProperties.DigestEnum String[] digests;
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes;
int keymasterSwEnforcedUserAuthenticators;
int keymasterHwEnforcedUserAuthenticators;
try {
@@ -105,10 +105,10 @@
List<String> encryptionPaddingsList = new ArrayList<String>();
for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) {
- String jcaPadding;
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String jcaPadding;
try {
- jcaPadding = KeymasterUtils.getJcaEncryptionPaddingFromKeymasterPadding(
- keymasterPadding);
+ jcaPadding =
+ KeyStoreKeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
} catch (IllegalArgumentException e) {
throw new InvalidKeySpecException(
"Unsupported encryption padding: " + keymasterPadding);
@@ -118,9 +118,9 @@
encryptionPaddings =
encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
- digests = KeymasterUtils.getJcaDigestAlgorithmsFromKeymasterDigests(
+ digests = KeyStoreKeyProperties.Digest.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST));
- blockModes = KeymasterUtils.getJcaBlockModesFromKeymasterBlockModes(
+ blockModes = KeyStoreKeyProperties.BlockMode.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_BLOCK_MODE));
keymasterSwEnforcedUserAuthenticators =
keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
index aa44ecd..df67ae7 100644
--- a/keystore/java/android/security/KeymasterUtils.java
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -21,11 +21,6 @@
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
-import libcore.util.EmptyArray;
-
-import java.util.Collection;
-import java.util.Locale;
-
/**
* @hide
*/
@@ -33,152 +28,6 @@
private KeymasterUtils() {}
- 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 {
- 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 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 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 int getDigestOutputSizeBits(int keymasterDigest) {
switch (keymasterDigest) {
case KeymasterDefs.KM_DIGEST_NONE:
@@ -200,60 +49,6 @@
}
}
- 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 {
- 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:
@@ -267,82 +62,6 @@
}
}
- 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;
- }
-
/**
* Adds keymaster arguments to express the key's authorization policy supported by user
* authentication.
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index fd0c06d..d3df151 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -20,6 +20,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.provider.Settings;
import android.text.TextUtils;
@@ -151,14 +152,14 @@
for (ResolveInfo resolveInfo : resolveInfoList) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
+ if (activityInfo != null && !packageNames.contains(activityInfo.packageName)) {
+ packageNames.add(activityInfo.packageName);
}
- packageNames.add(activityInfo.packageName);
}
- // TODO: Filter for apps that don't handle DIAL intent with tel scheme
- return packageNames;
+ final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL);
+ dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null));
+ return filterByIntent(context, packageNames, dialIntentWithTelScheme);
}
/**
@@ -182,6 +183,36 @@
|| packageName.equals(tm.getSystemDialerPackage());
}
+ /**
+ * Filter a given list of package names for those packages that contain an activity that has
+ * an intent filter for a given intent.
+ *
+ * @param context A valid context
+ * @param packageNames List of package names to filter.
+ * @return The filtered list.
+ */
+ private static List<String> filterByIntent(Context context, List<String> packageNames,
+ Intent intent) {
+ if (packageNames == null || packageNames.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ final List<String> result = new ArrayList<>();
+ final List<ResolveInfo> resolveInfoList =
+ context.getPackageManager().queryIntentActivities(intent, 0);
+ final int length = resolveInfoList.size();
+ for (int i = 0; i < length; i++) {
+ final ActivityInfo info = resolveInfoList.get(i).activityInfo;
+ if (info != null && packageNames.contains(info.packageName)
+ && !result.contains(info.packageName)) {
+ result.add(info.packageName);
+ }
+ }
+
+ return result;
+ }
+
+
private static TelecomManager getTelecomManager(Context context) {
return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
}