Merge "Keystore 2.0: Fix diagnosing invalid key in CipherSpiBase." into sc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index e1cb5e4..8bc603d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -52315,7 +52315,7 @@
method @NonNull public java.util.List<android.view.textservice.SpellCheckerInfo> getEnabledSpellCheckerInfos();
method public boolean isSpellCheckerEnabled();
method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable android.os.Bundle, @Nullable java.util.Locale, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean);
- method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable android.os.Bundle, @Nullable java.util.Locale, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean, int);
+ method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable java.util.Locale, boolean, int, @Nullable android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener);
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 579f912..d4d3321 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6169,13 +6169,22 @@
// STOPSHIP(b/174298501): clarify the expected return value following generateKeyPair call.
/**
- * Called by a device or profile owner, or delegated certificate installer, to query whether a
- * certificate and private key are installed under a given alias.
+ * This API can be called by the following to query whether a certificate and private key are
+ * installed under a given alias:
+ * <ul>
+ * <li>Device owner</li>
+ * <li>Profile owner</li>
+ * <li>Delegated certificate installer</li>
+ * <li>Credential management app</li>
+ * </ul>
+ *
+ * If called by the credential management app, the alias must exist in the credential
+ * management app's {@link android.security.AppUriAuthenticationPolicy}.
*
* @param alias The alias under which the key pair is installed.
* @return {@code true} if a key pair with this alias exists, {@code false} otherwise.
- * @throws SecurityException if the caller is not a device or profile owner or a delegated
- * certificate installer.
+ * @throws SecurityException if the caller is not a device or profile owner, a delegated
+ * certificate installer or the credential management app.
* @see #setDelegatedScopes
* @see #DELEGATION_CERT_INSTALL
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f0b22a9..13f3979 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -15835,6 +15835,7 @@
*
* @hide
*/
+ @Readable
public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches";
/**
@@ -15860,6 +15861,7 @@
*
* @hide
*/
+ @Readable
public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
"maximum_obscuring_opacity_for_touch";
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index ba58b65..a449cf1 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -16,6 +16,8 @@
package android.view.textservice;
+import android.annotation.BinderThread;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Binder;
import android.os.Build;
@@ -27,6 +29,7 @@
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.textservice.ISpellCheckerSession;
import com.android.internal.textservice.ISpellCheckerSessionListener;
import com.android.internal.textservice.ITextServicesSessionListener;
@@ -35,6 +38,7 @@
import java.util.LinkedList;
import java.util.Queue;
+import java.util.concurrent.Executor;
/**
* The SpellCheckerSession interface provides the per client functionality of SpellCheckerService.
@@ -102,38 +106,26 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final SpellCheckerSessionListener mSpellCheckerSessionListener;
private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl;
+ private final Executor mExecutor;
private final CloseGuard mGuard = CloseGuard.get();
- /** Handler that will execute the main tasks */
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ON_GET_SUGGESTION_MULTIPLE:
- handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj);
- break;
- case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE:
- handleOnGetSentenceSuggestionsMultiple((SentenceSuggestionsInfo[]) msg.obj);
- break;
- }
- }
- };
-
/**
* Constructor
* @hide
*/
public SpellCheckerSession(
- SpellCheckerInfo info, TextServicesManager tsm, SpellCheckerSessionListener listener) {
+ SpellCheckerInfo info, TextServicesManager tsm, SpellCheckerSessionListener listener,
+ Executor executor) {
if (info == null || listener == null || tsm == null) {
throw new NullPointerException();
}
mSpellCheckerInfo = info;
- mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(mHandler);
+ mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(this);
mInternalListener = new InternalListener(mSpellCheckerSessionListenerImpl);
mTextServicesManager = tsm;
mSpellCheckerSessionListener = listener;
+ mExecutor = executor;
mGuard.open("finishSession");
}
@@ -219,12 +211,13 @@
textInfos, suggestionsLimit, sequentialWords);
}
- private void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionInfos) {
- mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos);
+ void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionsInfos) {
+ mExecutor.execute(() -> mSpellCheckerSessionListener.onGetSuggestions(suggestionsInfos));
}
- private void handleOnGetSentenceSuggestionsMultiple(SentenceSuggestionsInfo[] suggestionInfos) {
- mSpellCheckerSessionListener.onGetSentenceSuggestions(suggestionInfos);
+ void handleOnGetSentenceSuggestionsMultiple(SentenceSuggestionsInfo[] suggestionsInfos) {
+ mExecutor.execute(() ->
+ mSpellCheckerSessionListener.onGetSentenceSuggestions(suggestionsInfos));
}
private static final class SpellCheckerSessionListenerImpl
@@ -249,7 +242,8 @@
}
private final Queue<SpellCheckerParams> mPendingTasks = new LinkedList<>();
- private Handler mHandler;
+ @GuardedBy("SpellCheckerSessionListenerImpl.this")
+ private SpellCheckerSession mSpellCheckerSession;
private static final int STATE_WAIT_CONNECTION = 0;
private static final int STATE_CONNECTED = 1;
@@ -270,8 +264,8 @@
private HandlerThread mThread;
private Handler mAsyncHandler;
- public SpellCheckerSessionListenerImpl(Handler handler) {
- mHandler = handler;
+ SpellCheckerSessionListenerImpl(SpellCheckerSession spellCheckerSession) {
+ mSpellCheckerSession = spellCheckerSession;
}
private static class SpellCheckerParams {
@@ -349,6 +343,7 @@
}
}
+ @GuardedBy("SpellCheckerSessionListenerImpl.this")
private void processCloseLocked() {
if (DBG) Log.d(TAG, "entering processCloseLocked:"
+ " session" + (mISpellCheckerSession != null ? ".hashCode()=#"
@@ -358,7 +353,7 @@
if (mThread != null) {
mThread.quit();
}
- mHandler = null;
+ mSpellCheckerSession = null;
mPendingTasks.clear();
mThread = null;
mAsyncHandler = null;
@@ -502,23 +497,30 @@
processTask(session, scp, false);
}
+ @BinderThread
@Override
public void onGetSuggestions(SuggestionsInfo[] results) {
- synchronized (this) {
- if (mHandler != null) {
- mHandler.sendMessage(Message.obtain(mHandler,
- MSG_ON_GET_SUGGESTION_MULTIPLE, results));
- }
+ SpellCheckerSession session = getSpellCheckerSession();
+ if (session != null) {
+ // Lock should not be held when calling callback, in order to avoid deadlock.
+ session.handleOnGetSuggestionsMultiple(results);
}
}
+ @BinderThread
@Override
public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {
- synchronized (this) {
- if (mHandler != null) {
- mHandler.sendMessage(Message.obtain(mHandler,
- MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results));
- }
+ SpellCheckerSession session = getSpellCheckerSession();
+ if (session != null) {
+ // Lock should not be held when calling callback, in order to avoid deadlock.
+ session.handleOnGetSentenceSuggestionsMultiple(results);
+ }
+ }
+
+ @Nullable
+ private SpellCheckerSession getSpellCheckerSession() {
+ synchronized (SpellCheckerSessionListenerImpl.this) {
+ return mSpellCheckerSession;
}
}
}
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 4f6fa27..bf91cca 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -16,6 +16,7 @@
package android.view.textservice;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -25,6 +26,8 @@
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -40,6 +43,8 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* System API to the overall text services, which arbitrates interaction between applications
@@ -161,10 +166,12 @@
* {@link SuggestionsInfo#RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS} will be passed to the spell
* checker as supported attributes.
*
- * @see #newSpellCheckerSession(Bundle, Locale, SpellCheckerSessionListener, boolean, int)
+ * @see #newSpellCheckerSession(Locale, boolean, int, Bundle, Executor,
+ * SpellCheckerSessionListener)
* @param bundle A bundle to pass to the spell checker.
* @param locale The locale for the spell checker.
* @param listener A spell checker session lister for getting results from the spell checker.
+ * The listener will be called on the calling thread.
* @param referToSpellCheckerLanguageSettings If true, the session for one of enabled
* languages in settings will be used.
* @return A spell checker session from the spell checker.
@@ -174,10 +181,15 @@
@Nullable Locale locale,
@NonNull SpellCheckerSessionListener listener,
boolean referToSpellCheckerLanguageSettings) {
- return newSpellCheckerSession(bundle, locale, listener, referToSpellCheckerLanguageSettings,
- SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
- | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
- | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS);
+ // Attributes existed before {@link #newSpellCheckerSession(Locale, boolean, int, Bundle,
+ // Executor, SpellCheckerSessionListener)} was introduced.
+ int supportedAttributes = SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
+ | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
+ | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS;
+ // Using the implicit looper to preserve the old behavior.
+ Executor executor = new HandlerExecutor(new Handler());
+ return newSpellCheckerSession(locale, referToSpellCheckerLanguageSettings,
+ supportedAttributes, bundle, executor, listener);
}
/**
@@ -191,25 +203,28 @@
* language only (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
* selected.
*
- * @param bundle A bundle to pass to the spell checker.
* @param locale The locale for the spell checker.
- * @param listener A spell checker session lister for getting results from a spell checker.
* @param referToSpellCheckerLanguageSettings If true, the session for one of enabled
* languages in settings will be used.
* @param supportedAttributes A union of {@link SuggestionsInfo} attributes that the spell
* checker can set in the spell checking results.
+ * @param bundle A bundle for passing implementation-specific extra parameters for the spell
+ * checker. You can check the current spell checker package by
+ * {@link #getCurrentSpellCheckerInfo()}.
+ * @param executor An executor to call the listener on.
+ * @param listener A spell checker session lister for getting results from a spell checker.
* @return The spell checker session of the spell checker.
*/
@Nullable
public SpellCheckerSession newSpellCheckerSession(
- @SuppressLint("NullableCollection") @Nullable Bundle bundle,
@SuppressLint("UseIcu") @Nullable Locale locale,
- @NonNull SpellCheckerSessionListener listener,
- @SuppressLint("ListenerLast") boolean referToSpellCheckerLanguageSettings,
- @SuppressLint("ListenerLast") @SuggestionsInfo.ResultAttrs int supportedAttributes) {
- if (listener == null) {
- throw new NullPointerException();
- }
+ boolean referToSpellCheckerLanguageSettings,
+ @SuggestionsInfo.ResultAttrs int supportedAttributes,
+ @Nullable Bundle bundle,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull SpellCheckerSessionListener listener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
if (!referToSpellCheckerLanguageSettings && locale == null) {
throw new IllegalArgumentException("Locale should not be null if you don't refer"
+ " settings.");
@@ -259,7 +274,7 @@
if (subtypeInUse == null) {
return null;
}
- final SpellCheckerSession session = new SpellCheckerSession(sci, this, listener);
+ final SpellCheckerSession session = new SpellCheckerSession(sci, this, listener, executor);
try {
mService.getSpellCheckerService(mUserId, sci.getId(), subtypeInUse.getLocale(),
session.getTextServicesSessionListener(),
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 2464b4a..a63305e 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -126,14 +126,14 @@
|| mTextServicesManager.getCurrentSpellCheckerSubtype(true) == null) {
mSpellCheckerSession = null;
} else {
+ int supportedAttributes = SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
+ | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
+ | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR
+ | SuggestionsInfo.RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS;
mSpellCheckerSession = mTextServicesManager.newSpellCheckerSession(
+ mCurrentLocale, false, supportedAttributes,
null /* Bundle not currently used by the textServicesManager */,
- mCurrentLocale, this,
- false /* means any available languages from current spell checker */,
- SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
- | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
- | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR
- | SuggestionsInfo.RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS);
+ mTextView.getContext().getMainExecutor(), this);
}
// Restore SpellCheckSpans in pool
diff --git a/data/etc/car/com.android.car.messenger.xml b/data/etc/car/com.android.car.messenger.xml
index 16595c3..9e5f339 100644
--- a/data/etc/car/com.android.car.messenger.xml
+++ b/data/etc/car/com.android.car.messenger.xml
@@ -16,6 +16,7 @@
-->
<permissions>
<privapp-permissions package="com.android.car.messenger">
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
</privapp-permissions>
</permissions>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
index 8efe053..73b02f4 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
@@ -18,33 +18,20 @@
android:paddingMode="stack" >
<item android:id="@android:id/background"
android:gravity="center_vertical|fill_horizontal">
- <layer-list>
- <item>
- <shape
- android:tint="?android:attr/colorControlActivated"
- android:alpha="?android:attr/disabledAlpha">
- <size android:height="@dimen/rounded_slider_height" />
- <solid android:color="@color/white_disabled" />
- <corners android:radius="@dimen/rounded_slider_corner_radius" />
- </shape>
- </item>
- <item
- android:gravity="center_vertical|left"
- android:height="@dimen/rounded_slider_icon_size"
- android:width="@dimen/rounded_slider_icon_size"
- android:left="@dimen/rounded_slider_icon_inset">
- <com.android.systemui.util.AlphaTintDrawableWrapper
- android:drawable="@drawable/ic_brightness"
- android:tint="?android:attr/colorControlActivated" />
- </item>
- </layer-list>
+ <inset
+ android:insetLeft="@dimen/rounded_slider_track_inset"
+ android:insetRight="@dimen/rounded_slider_track_inset" >
+ <shape>
+ <size android:height="@dimen/rounded_slider_track_width" />
+ <corners android:radius="@dimen/rounded_slider_track_corner_radius" />
+ <solid android:color="?android:attr/textColorPrimary" />
+ </shape>
+ </inset>
</item>
<item android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
- <clip
+ <com.android.systemui.util.RoundedCornerProgressDrawable
android:drawable="@drawable/brightness_progress_full_drawable"
- android:clipOrientation="horizontal"
- android:gravity="left"
/>
</item>
</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
index 5bc2773..41140a7 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
@@ -26,10 +26,10 @@
</item>
<item
android:id="@+id/slider_icon"
- android:gravity="center_vertical|left"
+ android:gravity="center_vertical|right"
android:height="@dimen/rounded_slider_icon_size"
android:width="@dimen/rounded_slider_icon_size"
- android:left="@dimen/rounded_slider_icon_inset">
+ android:right="@dimen/rounded_slider_icon_inset">
<com.android.systemui.util.AlphaTintDrawableWrapper
android:drawable="@drawable/ic_brightness"
android:tint="?android:attr/colorBackground"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2062104..6e270a7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1356,11 +1356,15 @@
<dimen name="people_space_image_radius">20dp</dimen>
<dimen name="people_space_widget_background_padding">6dp</dimen>
- <dimen name="rounded_slider_height">48dp</dimen>
+ <dimen name="rounded_slider_height">44dp</dimen>
<!-- rounded_slider_height / 2 -->
- <dimen name="rounded_slider_corner_radius">24dp</dimen>
- <!-- rounded_slider_height / 2 -->
- <dimen name="rounded_slider_icon_size">24dp</dimen>
- <!-- rounded_slider_icon_size / 2 -->
+ <dimen name="rounded_slider_corner_radius">22dp</dimen>
+ <dimen name="rounded_slider_icon_size">20dp</dimen>
+ <!-- (rounded_slider_height - rounded_slider_icon_size) / 2 -->
<dimen name="rounded_slider_icon_inset">12dp</dimen>
+ <!-- rounded_slider_corner_radius - rounded_slider_track_corner_radius -->
+ <dimen name="rounded_slider_track_inset">18dp</dimen>
+ <dimen name="rounded_slider_track_width">8dp</dimen>
+ <!-- rounded_slider_track_width / 2 -->
+ <dimen name="rounded_slider_track_corner_radius">4dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
index 6aadd10..dc86d58 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
@@ -17,8 +17,6 @@
package com.android.systemui.util
import android.content.res.Resources
-import android.graphics.Canvas
-import android.graphics.Path
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.DrawableWrapper
@@ -43,53 +41,25 @@
private const val MAX_LEVEL = 10000 // Taken from Drawable
}
- private var clipPath: Path = Path()
-
- init {
- setClipPath(Rect())
- }
-
override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean {
onLevelChange(level)
return super.onLayoutDirectionChanged(layoutDirection)
}
override fun onBoundsChange(bounds: Rect) {
- setClipPath(bounds)
super.onBoundsChange(bounds)
onLevelChange(level)
}
- private fun setClipPath(bounds: Rect) {
- clipPath.reset()
- clipPath.addRoundRect(
- bounds.left.toFloat(),
- bounds.top.toFloat(),
- bounds.right.toFloat(),
- bounds.bottom.toFloat(),
- bounds.height().toFloat() / 2,
- bounds.height().toFloat() / 2,
- Path.Direction.CW
- )
- }
-
override fun onLevelChange(level: Int): Boolean {
val db = drawable?.bounds!!
- val width = bounds.width() * level / MAX_LEVEL
- // Extra space on the left to keep the rounded shape on the right end
- val leftBound = bounds.left - bounds.height()
- drawable?.setBounds(leftBound, db.top, bounds.left + width, db.bottom)
+ // On 0, the width is bounds.height (a circle), and on MAX_LEVEL, the width is bounds.width
+ val width = bounds.height() + (bounds.width() - bounds.height()) * level / MAX_LEVEL
+ drawable?.setBounds(bounds.left, db.top, bounds.left + width, db.bottom)
return super.onLevelChange(level)
}
- override fun draw(canvas: Canvas) {
- canvas.save()
- canvas.clipPath(clipPath)
- super.draw(canvas)
- canvas.restore()
- }
-
- override fun getConstantState(): ConstantState? {
+ override fun getConstantState(): ConstantState {
// This should not be null as it was created with a state in the constructor.
return RoundedCornerState(super.getConstantState()!!)
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 10b33b3..f2b4529 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5482,7 +5482,8 @@
@Override
public boolean hasKeyPair(String callerPackage, String alias) {
final CallerIdentity caller = getCallerIdentity(callerPackage);
- Preconditions.checkCallAuthorization(canManageCertificates(caller));
+ Preconditions.checkCallAuthorization(canManageCertificates(caller)
+ || isCredentialManagementApp(caller, alias));
return mInjector.binderWithCleanCallingIdentity(() -> {
try (KeyChainConnection keyChainConnection =