Merge changes from topics "dpmapis1", "dpmapis2"
* changes:
Add DPM Permissions 3.
Add permissions to DPM 2.
Add permission access to DPM Apis.
diff --git a/core/api/current.txt b/core/api/current.txt
index e350f56..95729da 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -22527,6 +22527,7 @@
field @Deprecated public static final int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
field public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2; // 0x2
+ field public static final int CONFIGURE_FLAG_USE_CRYPTO_ASYNC = 4; // 0x4
field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2
field public static final int CRYPTO_MODE_AES_CTR = 1; // 0x1
field public static final int CRYPTO_MODE_UNENCRYPTED = 0; // 0x0
@@ -22556,6 +22557,7 @@
public abstract static class MediaCodec.Callback {
ctor public MediaCodec.Callback();
+ method public void onCryptoError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CryptoException);
method public abstract void onError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CodecException);
method public abstract void onInputBufferAvailable(@NonNull android.media.MediaCodec, int);
method public abstract void onOutputBufferAvailable(@NonNull android.media.MediaCodec, int, @NonNull android.media.MediaCodec.BufferInfo);
@@ -22573,6 +22575,7 @@
public static final class MediaCodec.CryptoException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable {
ctor public MediaCodec.CryptoException(int, @Nullable String);
+ method @Nullable public android.media.MediaCodec.CryptoInfo getCryptoInfo();
method public int getErrorCode();
field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
field @Deprecated public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
@@ -51725,6 +51728,7 @@
ctor public SurfaceView(android.content.Context, android.util.AttributeSet);
ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int);
ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int, int);
+ method public void applyTransactionToFrame(@NonNull android.view.SurfaceControl.Transaction);
method public android.view.SurfaceHolder getHolder();
method @Nullable public android.os.IBinder getHostToken();
method public android.view.SurfaceControl getSurfaceControl();
diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS
index debb493..bb0e6ab 100644
--- a/core/java/android/service/notification/OWNERS
+++ b/core/java/android/service/notification/OWNERS
@@ -1,6 +1,7 @@
# Bug component: 34005
juliacr@google.com
-pixel@google.com
+yurilin@google.com
+jeffdq@google.com
dsandler@android.com
dsandler@google.com
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0bce710..b46a68c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1756,8 +1756,14 @@
};
/**
- * Return a SurfaceControl which can be used for parenting Surfaces to
- * this SurfaceView.
+ * Return a SurfaceControl which can be used for parenting Surfaces to this SurfaceView.
+ *
+ * Note that this SurfaceControl is effectively read-only. Its only well-defined usage is in
+ * using the SurfaceControl as a parent for an application's hierarchy of SurfaceControls. All
+ * other properties of the SurfaceControl, such as its position, may be mutated by the
+ * SurfaceView at any time which will override what the application is requesting. Do not apply
+ * any {@link SurfaceControl.Transaction} to this SurfaceControl except for reparenting
+ * child SurfaceControls. See: {@link SurfaceControl.Transaction#reparent}.
*
* @return The SurfaceControl for this SurfaceView.
*/
@@ -1983,4 +1989,32 @@
public void syncNextFrame(Consumer<Transaction> t) {
mBlastBufferQueue.syncNextTransaction(t);
}
+
+ /**
+ * Adds a transaction that would be applied synchronously with displaying the SurfaceView's next
+ * frame.
+ *
+ * Note that the exact frame that the transaction is applied with is only well-defined when
+ * SurfaceView rendering is paused prior to calling applyTransactionToFrame(), so that the
+ * transaction is applied with the next frame rendered after applyTransactionToFrame() is
+ * called. If frames are continuously rendering to the SurfaceView when
+ * applyTransactionToFrame() is called, then it is undefined which frame the transaction is
+ * applied with. It is also possible for the transaction to not be applied if no new frames are
+ * rendered to the SurfaceView after this is called.
+ *
+ * @param transaction The transaction to apply. The system takes ownership of the transaction
+ * and promises to eventually apply the transaction.
+ * @throws IllegalStateException if the underlying Surface does not exist (and therefore
+ * there is no next frame).
+ */
+ public void applyTransactionToFrame(@NonNull SurfaceControl.Transaction transaction) {
+ synchronized (mSurfaceControlLock) {
+ if (mBlastBufferQueue == null) {
+ throw new IllegalStateException("Surface does not exist!");
+ }
+
+ long frameNumber = mBlastBufferQueue.getLastAcquiredFrameNum() + 1;
+ mBlastBufferQueue.mergeWithNextTransaction(transaction, frameNumber);
+ }
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 480abe0..ff0e326 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10718,7 +10718,8 @@
public int ensureDirectConnection() {
if (mDirectConnectionId == AccessibilityNodeInfo.UNDEFINED_CONNECTION_ID) {
mDirectConnectionId = AccessibilityInteractionClient.addDirectConnection(
- new AccessibilityInteractionConnection(ViewRootImpl.this));
+ new AccessibilityInteractionConnection(ViewRootImpl.this),
+ mAccessibilityManager);
// Notify listeners in the app process.
mAccessibilityManager.notifyAccessibilityStateChanged();
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 3da3ab9..d9fcfb5 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -275,7 +275,8 @@
*
* @param connection The ViewRootImpl's {@link IAccessibilityInteractionConnection}.
*/
- public static int addDirectConnection(IAccessibilityInteractionConnection connection) {
+ public static int addDirectConnection(IAccessibilityInteractionConnection connection,
+ AccessibilityManager accessibilityManager) {
synchronized (sConnectionCache) {
int connectionId = sDirectConnectionIdCounter++;
if (getConnection(connectionId) != null) {
@@ -283,7 +284,7 @@
"Cannot add direct connection with existing id " + connectionId);
}
DirectAccessibilityConnection directAccessibilityConnection =
- new DirectAccessibilityConnection(connection);
+ new DirectAccessibilityConnection(connection, accessibilityManager);
sConnectionCache.put(connectionId, directAccessibilityConnection);
sDirectConnectionCount++;
// Do not use AccessibilityCache for this connection, since there is no corresponding
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index d405c0b..aa631cf 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -2431,4 +2431,31 @@
return true;
}
}
+
+ /**
+ * Retrieves the window's transformation matrix and magnification spec.
+ *
+ * <p>
+ * Used by callers outside of the AccessibilityManagerService process which need
+ * this information, like {@link android.view.accessibility.DirectAccessibilityConnection}.
+ * </p>
+ *
+ * @return The transformation spec
+ * @hide
+ */
+ public IAccessibilityManager.WindowTransformationSpec getWindowTransformationSpec(
+ int windowId) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return null;
+ }
+ }
+ try {
+ return service.getWindowTransformationSpec(windowId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/view/accessibility/DirectAccessibilityConnection.java b/core/java/android/view/accessibility/DirectAccessibilityConnection.java
index dcc5660..8a3bb6f 100644
--- a/core/java/android/view/accessibility/DirectAccessibilityConnection.java
+++ b/core/java/android/view/accessibility/DirectAccessibilityConnection.java
@@ -17,12 +17,10 @@
package android.view.accessibility;
import android.accessibilityservice.IAccessibilityServiceConnection;
-import android.graphics.Matrix;
import android.graphics.Region;
import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
-import android.view.MagnificationSpec;
/**
* Minimal {@link IAccessibilityServiceConnection} implementation that interacts
@@ -51,24 +49,21 @@
*/
class DirectAccessibilityConnection extends IAccessibilityServiceConnection.Default {
private final IAccessibilityInteractionConnection mAccessibilityInteractionConnection;
+ private final AccessibilityManager mAccessibilityManager;
// Fetch all views, but do not use prefetching/cache since this "connection" does not
// receive cache invalidation events (as it is not linked to an AccessibilityService).
private static final int FETCH_FLAGS =
AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS
| AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
- private static final MagnificationSpec MAGNIFICATION_SPEC = new MagnificationSpec();
private static final int PID = Process.myPid();
private static final Region INTERACTIVE_REGION = null;
- private static final float[] TRANSFORM_MATRIX = new float[9];
-
- static {
- Matrix.IDENTITY_MATRIX.getValues(TRANSFORM_MATRIX);
- }
DirectAccessibilityConnection(
- IAccessibilityInteractionConnection accessibilityInteractionConnection) {
+ IAccessibilityInteractionConnection accessibilityInteractionConnection,
+ AccessibilityManager accessibilityManager) {
mAccessibilityInteractionConnection = accessibilityInteractionConnection;
+ mAccessibilityManager = accessibilityManager;
}
@Override
@@ -76,9 +71,11 @@
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
Bundle arguments) throws RemoteException {
+ IAccessibilityManager.WindowTransformationSpec spec =
+ mAccessibilityManager.getWindowTransformationSpec(accessibilityWindowId);
mAccessibilityInteractionConnection.findAccessibilityNodeInfoByAccessibilityId(
accessibilityNodeId, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID,
- threadId, MAGNIFICATION_SPEC, TRANSFORM_MATRIX, arguments);
+ threadId, spec.magnificationSpec, spec.transformationMatrix, arguments);
return new String[0];
}
@@ -87,9 +84,11 @@
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId)
throws RemoteException {
+ IAccessibilityManager.WindowTransformationSpec spec =
+ mAccessibilityManager.getWindowTransformationSpec(accessibilityWindowId);
mAccessibilityInteractionConnection.findAccessibilityNodeInfosByText(accessibilityNodeId,
text, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
- MAGNIFICATION_SPEC, TRANSFORM_MATRIX);
+ spec.magnificationSpec, spec.transformationMatrix);
return new String[0];
}
@@ -98,9 +97,11 @@
long accessibilityNodeId, String viewId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId)
throws RemoteException {
+ IAccessibilityManager.WindowTransformationSpec spec =
+ mAccessibilityManager.getWindowTransformationSpec(accessibilityWindowId);
mAccessibilityInteractionConnection.findAccessibilityNodeInfosByViewId(accessibilityNodeId,
viewId, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
- MAGNIFICATION_SPEC, TRANSFORM_MATRIX);
+ spec.magnificationSpec, spec.transformationMatrix);
return new String[0];
}
@@ -108,9 +109,11 @@
public String[] findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId)
throws RemoteException {
+ IAccessibilityManager.WindowTransformationSpec spec =
+ mAccessibilityManager.getWindowTransformationSpec(accessibilityWindowId);
mAccessibilityInteractionConnection.findFocus(accessibilityNodeId, focusType,
INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
- MAGNIFICATION_SPEC, TRANSFORM_MATRIX);
+ spec.magnificationSpec, spec.transformationMatrix);
return new String[0];
}
@@ -118,9 +121,11 @@
public String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId)
throws RemoteException {
+ IAccessibilityManager.WindowTransformationSpec spec =
+ mAccessibilityManager.getWindowTransformationSpec(accessibilityWindowId);
mAccessibilityInteractionConnection.focusSearch(accessibilityNodeId, direction,
INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
- MAGNIFICATION_SPEC, TRANSFORM_MATRIX);
+ spec.magnificationSpec, spec.transformationMatrix);
return new String[0];
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 098e97c..1302421 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -29,6 +29,7 @@
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.InputEvent;
import android.view.IWindow;
+import android.view.MagnificationSpec;
/**
* Interface implemented by the AccessibilityManagerService called by
@@ -124,4 +125,10 @@
boolean startFlashNotificationSequence(String opPkg, int reason, IBinder token);
boolean stopFlashNotificationSequence(String opPkg);
boolean startFlashNotificationEvent(String opPkg, int reason, String reasonPkg);
+
+ parcelable WindowTransformationSpec {
+ float[] transformationMatrix;
+ MagnificationSpec magnificationSpec;
+ }
+ WindowTransformationSpec getWindowTransformationSpec(int windowId);
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index df9c539..2541a506 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -571,6 +571,10 @@
void onError(…) {
…
}
+ {@literal @Override}
+ void onCryptoError(…) {
+ …
+ }
});
codec.configure(format, …);
mOutputFormat = codec.getOutputFormat(); // option B
@@ -1774,6 +1778,7 @@
private static final int CB_OUTPUT_FORMAT_CHANGE = 4;
private static final String EOS_AND_DECODE_ONLY_ERROR_MESSAGE = "An input buffer cannot have "
+ "both BUFFER_FLAG_END_OF_STREAM and BUFFER_FLAG_DECODE_ONLY flags";
+ private static final int CB_CRYPTO_ERROR = 6;
private class EventHandler extends Handler {
private MediaCodec mCodec;
@@ -1901,6 +1906,12 @@
break;
}
+ case CB_CRYPTO_ERROR:
+ {
+ mCallback.onCryptoError(mCodec, (MediaCodec.CryptoException) msg.obj);
+ break;
+ }
+
case CB_OUTPUT_FORMAT_CHANGE:
{
mCallback.onOutputFormatChanged(mCodec,
@@ -2104,12 +2115,25 @@
*/
public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2;
+ /**
+ * This flag should be used on a secure decoder only. MediaCodec configured with this
+ * flag does decryption in a separate thread. The flag requires MediaCodec to operate
+ * asynchronously and will throw CryptoException if any, in the onCryptoError()
+ * callback. Applications should override the default implementation of
+ * onCryptoError() and access the associated CryptoException.
+ *
+ * CryptoException thrown will contain {@link MediaCodec.CryptoInfo}
+ * This can be accessed using getCryptoInfo()
+ */
+ public static final int CONFIGURE_FLAG_USE_CRYPTO_ASYNC = 4;
+
/** @hide */
@IntDef(
flag = true,
value = {
CONFIGURE_FLAG_ENCODE,
CONFIGURE_FLAG_USE_BLOCK_MODEL,
+ CONFIGURE_FLAG_USE_CRYPTO_ASYNC,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ConfigureFlag {}
@@ -2523,19 +2547,20 @@
public final static class CryptoException extends RuntimeException
implements MediaDrmThrowable {
public CryptoException(int errorCode, @Nullable String detailMessage) {
- this(detailMessage, errorCode, 0, 0, 0);
+ this(detailMessage, errorCode, 0, 0, 0, null);
}
/**
* @hide
*/
public CryptoException(String message, int errorCode, int vendorError, int oemError,
- int errorContext) {
+ int errorContext, @Nullable CryptoInfo cryptoInfo) {
super(message);
mErrorCode = errorCode;
mVendorError = vendorError;
mOemError = oemError;
mErrorContext = errorContext;
+ mCryptoInfo = cryptoInfo;
}
/**
@@ -2654,6 +2679,16 @@
return mErrorCode;
}
+ /**
+ * Returns CryptoInfo associated with this {@link CryptoException}
+ * if any
+ *
+ * @return CryptoInfo object if any. {@link MediaCodec.CryptoException}
+ */
+ public @Nullable CryptoInfo getCryptoInfo() {
+ return mCryptoInfo;
+ }
+
@Override
public int getVendorError() {
return mVendorError;
@@ -2670,6 +2705,7 @@
}
private final int mErrorCode, mVendorError, mOemError, mErrorContext;
+ private CryptoInfo mCryptoInfo;
}
/**
@@ -5088,6 +5124,25 @@
public abstract void onError(@NonNull MediaCodec codec, @NonNull CodecException e);
/**
+ * Called only when MediaCodec encountered a crypto(decryption) error when using
+ * a decoder configured with CONFIGURE_FLAG_USE_CRYPTO_ASYNC flag along with crypto
+ * or descrambler object.
+ *
+ * @param codec The MediaCodec object
+ * @param e The {@link MediaCodec.CryptoException} object with error details.
+ */
+ public void onCryptoError(@NonNull MediaCodec codec, @NonNull CryptoException e) {
+ /*
+ * A default implementation for backward compatibility.
+ * Use of CONFIGURE_FLAG_USE_CRYPTO_ASYNC requires override of this callback
+ * to receive CrytoInfo. Without an orverride an exception is thrown.
+ */
+ throw new IllegalStateException(
+ "Client must override onCryptoError when the codec is " +
+ "configured with CONFIGURE_FLAG_USE_CRYPTO_ASYNC.", e);
+ }
+
+ /**
* Called when the output format has changed
*
* @param codec The MediaCodec object.
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 5b0c2a2..9a4aa33 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -184,6 +184,8 @@
jmethodID postEventFromNativeID;
jmethodID lockAndGetContextID;
jmethodID setAndUnlockContextID;
+ jmethodID cryptoInfoSetID;
+ jmethodID cryptoInfoSetPatternID;
jfieldID cryptoInfoNumSubSamplesID;
jfieldID cryptoInfoNumBytesOfClearDataID;
jfieldID cryptoInfoNumBytesOfEncryptedDataID;
@@ -203,6 +205,7 @@
static fields_t gFields;
static const void *sRefBaseOwner;
+jint MediaErrorToJavaError(status_t err);
////////////////////////////////////////////////////////////////////////////////
@@ -1068,6 +1071,180 @@
return (jthrowable)env->NewObject(clazz.get(), ctor, err, actionCode, msgObj.get());
}
+static void AMessageToCryptoInfo(JNIEnv * env, const jobject & obj,
+ const sp<AMessage> & msg) {
+ if(msg == nullptr || obj == nullptr) {
+ ALOGE("CryptoAsync Nothing to do in AMessagetoCryptoInfo");
+ return;
+ }
+ size_t numSubSamples = 0;
+ sp<ABuffer> subSamplesBuffer;
+ sp<ABuffer> keyBuffer;
+ sp<ABuffer> ivBuffer;
+ CryptoPlugin::Mode mode;
+ CryptoPlugin::Pattern pattern;
+ CHECK(msg->findInt32("mode", (int*)&mode));
+ CHECK(msg->findSize("numSubSamples", &numSubSamples));
+ CHECK(msg->findBuffer("subSamples", &subSamplesBuffer));
+ CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks));
+ CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks));
+ CHECK(msg->findBuffer("iv", &ivBuffer));
+ CHECK(msg->findBuffer("key", &keyBuffer));
+
+ // subsamples
+ ScopedLocalRef<jintArray> samplesOfEncryptedDataArr(env, env->NewIntArray(numSubSamples));
+ ScopedLocalRef<jintArray> samplesOfClearDataArr(env, env->NewIntArray(numSubSamples));
+ jboolean isCopy;
+ jint *dstEncryptedSamples =
+ env->GetIntArrayElements(samplesOfEncryptedDataArr.get(), &isCopy);
+ jint * dstClearSamples =
+ env->GetIntArrayElements(samplesOfClearDataArr.get(), &isCopy);
+
+ CryptoPlugin::SubSample * samplesArray =
+ (CryptoPlugin::SubSample*)(subSamplesBuffer.get()->data());
+
+ for(int i = 0 ; i < numSubSamples ; i++) {
+ dstEncryptedSamples[i] = samplesArray[i].mNumBytesOfEncryptedData;
+ dstClearSamples[i] = samplesArray[i].mNumBytesOfClearData;
+ }
+ env->ReleaseIntArrayElements(samplesOfEncryptedDataArr.get(), dstEncryptedSamples, 0);
+ env->ReleaseIntArrayElements(samplesOfClearDataArr.get(), dstClearSamples, 0);
+ // key and iv
+ jbyteArray keyArray = NULL;
+ jbyteArray ivArray = NULL;
+ if (keyBuffer.get() != nullptr && keyBuffer->size() > 0) {
+ keyArray = env->NewByteArray(keyBuffer->size());
+ jbyte * dstKey = env->GetByteArrayElements(keyArray, &isCopy);
+ memcpy(dstKey, keyBuffer->data(), keyBuffer->size());
+ env->ReleaseByteArrayElements(keyArray,dstKey,0);
+ }
+ if (ivBuffer.get() != nullptr && ivBuffer->size() > 0) {
+ ivArray = env->NewByteArray(ivBuffer->size());
+ jbyte *dstIv = env->GetByteArrayElements(ivArray, &isCopy);
+ memcpy(dstIv, ivBuffer->data(), ivBuffer->size());
+ env->ReleaseByteArrayElements(ivArray, dstIv,0);
+ }
+ // set samples, key and iv
+ env->CallVoidMethod(
+ obj,
+ gFields.cryptoInfoSetID,
+ (jint)numSubSamples,
+ samplesOfClearDataArr.get(),
+ samplesOfEncryptedDataArr.get(),
+ keyArray,
+ ivArray,
+ mode);
+ if (keyArray != NULL) {
+ env->DeleteLocalRef(keyArray);
+ }
+ if (ivArray != NULL) {
+ env->DeleteLocalRef(ivArray);
+ }
+ // set pattern
+ env->CallVoidMethod(
+ obj,
+ gFields.cryptoInfoSetPatternID,
+ pattern.mEncryptBlocks,
+ pattern.mSkipBlocks);
+}
+
+static void CryptoErrorToJavaError(status_t err, jint& jerr, std::string& defaultMsg) {
+ switch(err) {
+ case ERROR_DRM_NO_LICENSE:
+ jerr = gCryptoErrorCodes.cryptoErrorNoKey;
+ defaultMsg = "Crypto key not available";
+ break;
+ case ERROR_DRM_LICENSE_EXPIRED:
+ jerr = gCryptoErrorCodes.cryptoErrorKeyExpired;
+ defaultMsg = "License expired";
+ break;
+ case ERROR_DRM_RESOURCE_BUSY:
+ jerr = gCryptoErrorCodes.cryptoErrorResourceBusy;
+ defaultMsg = "Resource busy or unavailable";
+ break;
+ case ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
+ jerr = gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection;
+ defaultMsg = "Required output protections are not active";
+ break;
+ case ERROR_DRM_SESSION_NOT_OPENED:
+ jerr = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
+ defaultMsg = "Attempted to use a closed session";
+ break;
+ case ERROR_DRM_INSUFFICIENT_SECURITY:
+ jerr = gCryptoErrorCodes.cryptoErrorInsufficientSecurity;
+ defaultMsg = "Required security level is not met";
+ break;
+ case ERROR_DRM_CANNOT_HANDLE:
+ jerr = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
+ defaultMsg = "Operation not supported in this configuration";
+ break;
+ case ERROR_DRM_FRAME_TOO_LARGE:
+ jerr = gCryptoErrorCodes.cryptoErrorFrameTooLarge;
+ defaultMsg = "Decrytped frame exceeds size of output buffer";
+ break;
+ case ERROR_DRM_SESSION_LOST_STATE:
+ jerr = gCryptoErrorCodes.cryptoErrorLostState;
+ defaultMsg = "Session state was lost, open a new session and retry";
+ break;
+ default: // Other negative DRM error codes go out best-effort.
+ jerr = MediaErrorToJavaError(err);
+ defaultMsg = StrCryptoError(err);
+ break;
+ }
+}
+static jthrowable createCryptoException(JNIEnv *env, status_t err,
+ const char * msg = NULL, const sp<ICrypto> & crypto = NULL,
+ const sp<AMessage> & cryptoInfo = NULL) {
+ jthrowable exception = nullptr;
+ jmethodID constructID = nullptr;
+ ScopedLocalRef<jobject> cryptoInfoObject(env);
+ std::string defaultMsg = "Unknown Error";
+ jint jerr = 0;
+ // Get a class ref for CryptoException
+ ScopedLocalRef<jclass> clazz(
+ env, env->FindClass("android/media/MediaCodec$CryptoException"));
+ CHECK(clazz.get() != NULL);
+
+ // Get constructor ref for CryptoException
+ constructID = env->GetMethodID(clazz.get(), "<init>",
+ "(Ljava/lang/String;IIIILandroid/media/MediaCodec$CryptoInfo;)V");
+ CHECK(constructID != NULL);
+
+ // create detailed message for exception
+ CryptoErrorToJavaError(err, jerr, defaultMsg);
+ std::string originalMsg(msg != NULL ? msg : defaultMsg.c_str());
+ DrmStatus dStatus(err, originalMsg.c_str());
+ std::string detailedMsg(
+ DrmUtils::GetExceptionMessage(dStatus, defaultMsg.c_str(), crypto));
+ jstring msgObj = env->NewStringUTF(detailedMsg.c_str());
+
+ if (cryptoInfo != nullptr) {
+ // Class ref for CryptoInfo
+ ScopedLocalRef<jclass> clazzCryptoInfo(
+ env, env->FindClass("android/media/MediaCodec$CryptoInfo"));
+ CHECK(clazzCryptoInfo.get() != NULL);
+
+ // Constructor reference for CryptoInfo
+ jmethodID constructCryptoInfo =
+ env->GetMethodID(clazzCryptoInfo.get(), "<init>", "()V");
+ CHECK(constructCryptoInfo != NULL);
+
+ // Create CryptoInfo jobject
+ cryptoInfoObject.reset(
+ env->NewObject(clazzCryptoInfo.get(), constructCryptoInfo));
+ CHECK(cryptoInfoObject.get() != NULL);
+
+ // Translate AMesage to CryptoInfo
+ AMessageToCryptoInfo(env, cryptoInfoObject.get(), cryptoInfo);
+ }
+
+ exception = (jthrowable)env->NewObject(
+ clazz.get(), constructID, msgObj, jerr,
+ dStatus.getCdmErr(), dStatus.getOemErr(), dStatus.getContext(),
+ cryptoInfoObject.get());
+
+ return exception;
+}
void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
int32_t arg1, arg2 = 0;
jobject obj = NULL;
@@ -1107,6 +1284,17 @@
break;
}
+ case MediaCodec::CB_CRYPTO_ERROR:
+ {
+ int32_t err, actionCode;
+ AString errorDetail;
+ CHECK(msg->findInt32("err", &err));
+ CHECK(msg->findInt32("actionCode",&actionCode));
+ CHECK(msg->findString("errorDetail", &errorDetail));
+ obj = (jobject)createCryptoException(env, err, errorDetail.c_str(), NULL, msg);
+ break;
+ }
+
case MediaCodec::CB_ERROR:
{
int32_t err, actionCode;
@@ -1144,7 +1332,6 @@
default:
TRESPASS();
}
-
env->CallVoidMethod(
mObject,
gFields.postEventFromNativeID,
@@ -1229,7 +1416,6 @@
}
}
-jint MediaErrorToJavaError(status_t err);
} // namespace android
@@ -1280,70 +1466,8 @@
static void throwCryptoException(JNIEnv *env, status_t err, const char *msg,
const sp<ICrypto> &crypto) {
- ScopedLocalRef<jclass> clazz(
- env, env->FindClass("android/media/MediaCodec$CryptoException"));
- CHECK(clazz.get() != NULL);
-
- jmethodID constructID =
- env->GetMethodID(clazz.get(), "<init>", "(Ljava/lang/String;IIII)V");
- CHECK(constructID != NULL);
-
- std::string defaultMsg = "Unknown Error";
-
- /* translate OS errors to Java API CryptoException errorCodes (which are positive) */
- jint jerr = 0;
- switch (err) {
- case ERROR_DRM_NO_LICENSE:
- jerr = gCryptoErrorCodes.cryptoErrorNoKey;
- defaultMsg = "Crypto key not available";
- break;
- case ERROR_DRM_LICENSE_EXPIRED:
- jerr = gCryptoErrorCodes.cryptoErrorKeyExpired;
- defaultMsg = "License expired";
- break;
- case ERROR_DRM_RESOURCE_BUSY:
- jerr = gCryptoErrorCodes.cryptoErrorResourceBusy;
- defaultMsg = "Resource busy or unavailable";
- break;
- case ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
- jerr = gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection;
- defaultMsg = "Required output protections are not active";
- break;
- case ERROR_DRM_SESSION_NOT_OPENED:
- jerr = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
- defaultMsg = "Attempted to use a closed session";
- break;
- case ERROR_DRM_INSUFFICIENT_SECURITY:
- jerr = gCryptoErrorCodes.cryptoErrorInsufficientSecurity;
- defaultMsg = "Required security level is not met";
- break;
- case ERROR_DRM_CANNOT_HANDLE:
- jerr = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
- defaultMsg = "Operation not supported in this configuration";
- break;
- case ERROR_DRM_FRAME_TOO_LARGE:
- jerr = gCryptoErrorCodes.cryptoErrorFrameTooLarge;
- defaultMsg = "Decrytped frame exceeds size of output buffer";
- break;
- case ERROR_DRM_SESSION_LOST_STATE:
- jerr = gCryptoErrorCodes.cryptoErrorLostState;
- defaultMsg = "Session state was lost, open a new session and retry";
- break;
- default: /* Other negative DRM error codes go out best-effort. */
- jerr = MediaErrorToJavaError(err);
- defaultMsg = StrCryptoError(err);
- break;
- }
-
- std::string originalMsg(msg != NULL ? msg : defaultMsg.c_str());
- DrmStatus dStatus(err, originalMsg.c_str());
- std::string detailedMsg(DrmUtils::GetExceptionMessage(dStatus, defaultMsg.c_str(), crypto));
- jstring msgObj = env->NewStringUTF(detailedMsg.c_str());
-
- jthrowable exception =
- (jthrowable)env->NewObject(clazz.get(), constructID, msgObj, jerr,
- dStatus.getCdmErr(), dStatus.getOemErr(), dStatus.getContext());
-
+ jthrowable exception = createCryptoException(
+ env, err, msg, crypto, /* cryptoInfo */ NULL);
env->Throw(exception);
}
@@ -2963,6 +3087,12 @@
clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
CHECK(clazz.get() != NULL);
+ gFields.cryptoInfoSetID = env->GetMethodID(clazz.get(), "set", "(I[I[I[B[BI)V");
+ CHECK(gFields.cryptoInfoSetID != NULL);
+
+ gFields.cryptoInfoSetPatternID = env->GetMethodID(clazz.get(), "setPattern", "(II)V");
+ CHECK(gFields.cryptoInfoSetPatternID != NULL);
+
gFields.cryptoInfoNumSubSamplesID =
env->GetFieldID(clazz.get(), "numSubSamples", "I");
CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 85e050c..ce7f0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -100,7 +100,7 @@
// TODO(b/260335638): Tracking Bug
@JvmField
val NOTIFICATION_INLINE_REPLY_ANIMATION =
- unreleasedFlag(174148361, "notification_inline_reply_animation", teamfood = true)
+ releasedFlag(174148361, "notification_inline_reply_animation")
val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
releasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 5dd532a..9d6230b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -1346,9 +1346,9 @@
if (keyguardUpdateMonitor.isUserInLockdown(removed.userId)) {
logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
} else if (useMediaResumption && removed.resumeAction != null && removed.isLocalSession()) {
- convertToResumePlayer(removed)
+ convertToResumePlayer(key, removed)
} else if (mediaFlags.isRetainingPlayersEnabled()) {
- handlePossibleRemoval(removed, notificationRemoved = true)
+ handlePossibleRemoval(key, removed, notificationRemoved = true)
} else {
notifyMediaDataRemoved(key)
logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
@@ -1362,7 +1362,7 @@
val entry = mediaEntries.remove(key) ?: return
// Clear token since the session is no longer valid
val updated = entry.copy(token = null)
- handlePossibleRemoval(updated)
+ handlePossibleRemoval(key, updated)
}
/**
@@ -1371,8 +1371,11 @@
* if it was removed before becoming inactive. (Assumes that [removed] was removed from
* [mediaEntries] before this function was called)
*/
- private fun handlePossibleRemoval(removed: MediaData, notificationRemoved: Boolean = false) {
- val key = removed.notificationKey!!
+ private fun handlePossibleRemoval(
+ key: String,
+ removed: MediaData,
+ notificationRemoved: Boolean = false
+ ) {
val hasSession = removed.token != null
if (hasSession && removed.semanticActions != null) {
// The app was using session actions, and the session is still valid: keep player
@@ -1398,13 +1401,12 @@
"($hasSession) gone for inactive player $key"
)
}
- convertToResumePlayer(removed)
+ convertToResumePlayer(key, removed)
}
}
/** Set the given [MediaData] as a resume state player and notify listeners */
- private fun convertToResumePlayer(data: MediaData) {
- val key = data.notificationKey!!
+ private fun convertToResumePlayer(key: String, data: MediaData) {
if (DEBUG) Log.d(TAG, "Converting $key to resume")
// Move to resume key (aka package name) if that key doesn't already exist.
val resumeAction = data.resumeAction?.let { getResumeMediaAction(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 6bf7668..82bd45c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.statusbar.notification.collection.coordinator
import android.os.UserHandle
@@ -39,14 +41,20 @@
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch
/**
@@ -93,14 +101,39 @@
private suspend fun trackUnseenNotificationsWhileUnlocked() {
// Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is
// showing again
+ var clearUnseenOnUnlock = false
keyguardRepository.isKeyguardShowing.collectLatest { isKeyguardShowing ->
- if (!isKeyguardShowing) {
+ if (isKeyguardShowing) {
+ // Wait for the user to spend enough time on the lock screen before clearing unseen
+ // set when unlocked
+ awaitTimeSpentNotDozing(SEEN_TIMEOUT)
+ clearUnseenOnUnlock = true
+ } else {
unseenNotifFilter.invalidateList("keyguard no longer showing")
+ if (clearUnseenOnUnlock) {
+ clearUnseenOnUnlock = false
+ unseenNotifications.clear()
+ }
trackUnseenNotifications()
}
}
}
+ private suspend fun awaitTimeSpentNotDozing(duration: Duration) {
+ keyguardRepository.isDozing
+ // Use transformLatest so that the timeout delay is cancelled if the device enters doze,
+ // and is restarted when doze ends.
+ .transformLatest { isDozing ->
+ if (!isDozing) {
+ delay(duration)
+ // Signal timeout has completed
+ emit(Unit)
+ }
+ }
+ // Suspend until the first emission
+ .first()
+ }
+
private suspend fun trackUnseenNotifications() {
coroutineScope {
launch { clearUnseenNotificationsWhenShadeIsExpanded() }
@@ -240,5 +273,6 @@
companion object {
private const val TAG = "KeyguardCoordinator"
+ private val SEEN_TIMEOUT = 5.seconds
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 9f12329..a07a714 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -1897,6 +1897,20 @@
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
}
+ @Test
+ fun testSessionDestroyed_noNotificationKey_stillRemoved() {
+ whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+
+ // When a notiifcation is added and then removed before it is fully processed
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ backgroundExecutor.runAllReady()
+ mediaDataManager.onNotificationRemoved(KEY)
+
+ // We still make sure to remove it
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ }
+
/** Helper function to add a media notification and capture the resulting MediaData */
private fun addNotificationAndLoad() {
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index 49da848..8109e24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -23,6 +23,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.advanceTimeBy
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
@@ -311,17 +312,20 @@
fun unseenNotificationIsMarkedAsSeenWhenKeyguardGoesAway() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
- // GIVEN: Keyguard is showing, unseen notification is present
+ // GIVEN: Keyguard is showing, not dozing, unseen notification is present
keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setDozing(false)
runKeyguardCoordinatorTest {
val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry)
+ // WHEN: five seconds have passed
+ testScheduler.advanceTimeBy(5.seconds)
+ testScheduler.runCurrent()
+
// WHEN: Keyguard is no longer showing
keyguardRepository.setKeyguardShowing(false)
-
- // When: Shade is expanded
- statusBarStateListener.onExpandedChanged(true)
+ testScheduler.runCurrent()
// WHEN: Keyguard is shown again
keyguardRepository.setKeyguardShowing(true)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/TestCoroutineSchedulerUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/TestCoroutineSchedulerUtils.kt
new file mode 100644
index 0000000..84e2a5c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/TestCoroutineSchedulerUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.coroutines
+
+import kotlin.time.Duration
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineScheduler
+
+/**
+ * Moves the virtual clock of this dispatcher forward by the specified [Duration].
+ *
+ * @see [TestCoroutineScheduler.advanceTimeBy]
+ */
+fun TestCoroutineScheduler.advanceTimeBy(duration: Duration) {
+ advanceTimeBy(duration.inWholeMilliseconds)
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 311e43a..72c5598 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -529,6 +529,18 @@
}
@Override
+ public IAccessibilityManager.WindowTransformationSpec getWindowTransformationSpec(
+ int windowId) {
+ IAccessibilityManager.WindowTransformationSpec windowTransformationSpec =
+ new IAccessibilityManager.WindowTransformationSpec();
+ Pair<float[], MagnificationSpec> result =
+ getWindowTransformationMatrixAndMagnificationSpec(windowId);
+ windowTransformationSpec.transformationMatrix = result.first;
+ windowTransformationSpec.magnificationSpec = result.second;
+ return windowTransformationSpec;
+ }
+
+ @Override
public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId,
userState.mBoundServices);
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 58c814a..e3d2189 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -741,6 +741,9 @@
final android.hardware.thermal.Temperature[] halRet =
shouldFilter ? mInstance.getTemperaturesWithType(type)
: mInstance.getTemperatures();
+ if (halRet == null) {
+ return ret;
+ }
for (android.hardware.thermal.Temperature t : halRet) {
if (!Temperature.isValidStatus(t.throttlingStatus)) {
Slog.e(TAG, "Invalid temperature status " + t.throttlingStatus
@@ -774,6 +777,9 @@
final android.hardware.thermal.CoolingDevice[] halRet = shouldFilter
? mInstance.getCoolingDevicesWithType(type)
: mInstance.getCoolingDevices();
+ if (halRet == null) {
+ return ret;
+ }
for (android.hardware.thermal.CoolingDevice t : halRet) {
if (!CoolingDevice.isValidType(t.type)) {
Slog.e(TAG, "Invalid cooling device type " + t.type + " from AIDL HAL");
@@ -806,9 +812,14 @@
final TemperatureThreshold[] halRet =
shouldFilter ? mInstance.getTemperatureThresholdsWithType(type)
: mInstance.getTemperatureThresholds();
-
- return Arrays.stream(halRet).filter(t -> t.type == type).collect(
- Collectors.toList());
+ if (halRet == null) {
+ return ret;
+ }
+ if (shouldFilter) {
+ return Arrays.stream(halRet).filter(t -> t.type == type).collect(
+ Collectors.toList());
+ }
+ return Arrays.asList(halRet);
} catch (IllegalArgumentException | IllegalStateException e) {
Slog.e(TAG, "Couldn't getTemperatureThresholds due to invalid status", e);
} catch (RemoteException e) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java
index 0ae8dfd..aa9d8c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java
@@ -120,6 +120,34 @@
}
@Test
+ public void getCurrentTemperatures_aidl() throws RemoteException {
+ android.hardware.thermal.Temperature halT1 = new android.hardware.thermal.Temperature();
+ halT1.type = TemperatureType.MODEM;
+ halT1.name = "test1";
+ halT1.throttlingStatus = ThrottlingSeverity.EMERGENCY;
+ halT1.value = 99.0f;
+ android.hardware.thermal.Temperature halT2 = new android.hardware.thermal.Temperature();
+ halT2.name = "test2";
+ halT2.type = TemperatureType.SOC;
+ halT2.throttlingStatus = ThrottlingSeverity.NONE;
+
+ Mockito.when(mAidlHalMock.getTemperatures()).thenReturn(
+ new android.hardware.thermal.Temperature[]{
+ halT2, halT1
+ });
+ List<Temperature> ret = mAidlWrapper.getCurrentTemperatures(false, TemperatureType.UNKNOWN);
+ Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatures();
+
+ Temperature expectedT1 = new Temperature(halT1.value, halT1.type, halT1.name,
+ halT1.throttlingStatus);
+ Temperature expectedT2 = new Temperature(halT2.value, halT2.type, halT2.name,
+ halT2.throttlingStatus);
+ List<Temperature> expectedRet = List.of(expectedT1, expectedT2);
+ assertTrue("Got temperature list as " + ret + " with different values compared to "
+ + expectedRet, expectedRet.containsAll(ret));
+ }
+
+ @Test
public void getCurrentTemperatures_withFilter_aidl() throws RemoteException {
android.hardware.thermal.Temperature halT1 = new android.hardware.thermal.Temperature();
halT1.type = TemperatureType.MODEM;
@@ -154,6 +182,23 @@
}
@Test
+ public void getCurrentTemperatures_nullResult_aidl() throws RemoteException {
+ Mockito.when(mAidlHalMock.getTemperaturesWithType(Mockito.anyInt())).thenReturn(null);
+ List<Temperature> ret = mAidlWrapper.getCurrentTemperatures(true,
+ Temperature.TYPE_SOC);
+ Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperaturesWithType(
+ Temperature.TYPE_SOC);
+ assertNotNull(ret);
+ assertEquals(0, ret.size());
+
+ Mockito.when(mAidlHalMock.getTemperatures()).thenReturn(null);
+ ret = mAidlWrapper.getCurrentTemperatures(false, Temperature.TYPE_SOC);
+ Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatures();
+ assertNotNull(ret);
+ assertEquals(0, ret.size());
+ }
+
+ @Test
public void getCurrentTemperatures_invalidStatus_aidl() throws RemoteException {
android.hardware.thermal.Temperature halTInvalid =
new android.hardware.thermal.Temperature();
@@ -210,6 +255,55 @@
}
@Test
+ public void getCurrentCoolingDevices_aidl() throws RemoteException {
+ android.hardware.thermal.CoolingDevice halC1 = new android.hardware.thermal.CoolingDevice();
+ halC1.type = CoolingType.SPEAKER;
+ halC1.name = "test1";
+ halC1.value = 10;
+ android.hardware.thermal.CoolingDevice halC2 = new android.hardware.thermal.CoolingDevice();
+ halC2.type = CoolingType.MODEM;
+ halC2.name = "test2";
+ halC2.value = 110;
+
+ Mockito.when(mAidlHalMock.getCoolingDevicesWithType(Mockito.anyInt())).thenReturn(
+ new android.hardware.thermal.CoolingDevice[]{
+ halC1, halC2
+ }
+ );
+ Mockito.when(mAidlHalMock.getCoolingDevices()).thenReturn(
+ new android.hardware.thermal.CoolingDevice[]{
+ halC1, halC2
+ }
+ );
+ List<CoolingDevice> ret = mAidlWrapper.getCurrentCoolingDevices(false,
+ CoolingType.COMPONENT);
+ Mockito.verify(mAidlHalMock, Mockito.times(1)).getCoolingDevices();
+
+ CoolingDevice expectedC1 = new CoolingDevice(halC1.value, halC1.type, halC1.name);
+ CoolingDevice expectedC2 = new CoolingDevice(halC2.value, halC2.type, halC2.name);
+ List<CoolingDevice> expectedRet = List.of(expectedC1, expectedC2);
+ assertTrue("Got cooling device list as " + ret + " with different values compared to "
+ + expectedRet, expectedRet.containsAll(ret));
+ }
+
+ @Test
+ public void getCurrentCoolingDevices_nullResult_aidl() throws RemoteException {
+ Mockito.when(mAidlHalMock.getCoolingDevicesWithType(Mockito.anyInt())).thenReturn(null);
+ List<CoolingDevice> ret = mAidlWrapper.getCurrentCoolingDevices(true,
+ CoolingType.COMPONENT);
+ Mockito.verify(mAidlHalMock, Mockito.times(1)).getCoolingDevicesWithType(
+ CoolingType.COMPONENT);
+ assertNotNull(ret);
+ assertEquals(0, ret.size());
+
+ Mockito.when(mAidlHalMock.getCoolingDevices()).thenReturn(null);
+ ret = mAidlWrapper.getCurrentCoolingDevices(false, CoolingType.COMPONENT);
+ Mockito.verify(mAidlHalMock, Mockito.times(1)).getCoolingDevices();
+ assertNotNull(ret);
+ assertEquals(0, ret.size());
+ }
+
+ @Test
public void getCurrentCoolingDevices_withFilter_aidl() throws RemoteException {
android.hardware.thermal.CoolingDevice halC1 = new android.hardware.thermal.CoolingDevice();
halC1.type = CoolingType.SPEAKER;
@@ -292,6 +386,44 @@
}
@Test
+ public void getTemperatureThresholds_aidl() throws RemoteException {
+ TemperatureThreshold halT1 = new TemperatureThreshold();
+ halT1.name = "test1";
+ halT1.type = Temperature.TYPE_SKIN;
+ halT1.hotThrottlingThresholds = new float[]{1, 2, 3};
+ halT1.coldThrottlingThresholds = new float[]{};
+
+ TemperatureThreshold halT2 = new TemperatureThreshold();
+ halT2.name = "test2";
+ halT2.type = Temperature.TYPE_SOC;
+ halT2.hotThrottlingThresholds = new float[]{};
+ halT2.coldThrottlingThresholds = new float[]{3, 2, 1};
+
+ Mockito.when(mAidlHalMock.getTemperatureThresholds()).thenReturn(
+ new TemperatureThreshold[]{halT1, halT2}
+ );
+ List<TemperatureThreshold> ret = mAidlWrapper.getTemperatureThresholds(false,
+ Temperature.TYPE_UNKNOWN);
+ Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatureThresholds();
+
+ assertEquals("Got unexpected temperature thresholds size", 2, ret.size());
+ TemperatureThreshold threshold = ret.get(0).type == Temperature.TYPE_SKIN ? ret.get(0)
+ : ret.get(1);
+
+ assertEquals(halT1.name, threshold.name);
+ assertEquals(halT1.type, threshold.type);
+ assertArrayEquals(halT1.hotThrottlingThresholds, threshold.hotThrottlingThresholds, 0.1f);
+ assertArrayEquals(halT1.coldThrottlingThresholds, threshold.coldThrottlingThresholds, 0.1f);
+
+ threshold = ret.get(0).type == Temperature.TYPE_SOC ? ret.get(0) : ret.get(1);
+
+ assertEquals(halT2.name, threshold.name);
+ assertEquals(halT2.type, threshold.type);
+ assertArrayEquals(halT2.hotThrottlingThresholds, threshold.hotThrottlingThresholds, 0.1f);
+ assertArrayEquals(halT2.coldThrottlingThresholds, threshold.coldThrottlingThresholds, 0.1f);
+ }
+
+ @Test
public void getTemperatureThresholds_withFilter_aidl() throws RemoteException {
TemperatureThreshold halT1 = new TemperatureThreshold();
halT1.name = "test1";
@@ -300,18 +432,18 @@
halT1.coldThrottlingThresholds = new float[]{};
TemperatureThreshold halT2 = new TemperatureThreshold();
- halT1.name = "test2";
- halT1.type = Temperature.TYPE_SOC;
- halT1.hotThrottlingThresholds = new float[]{};
- halT1.coldThrottlingThresholds = new float[]{3, 2, 1};
+ halT2.name = "test2";
+ halT2.type = Temperature.TYPE_SOC;
+ halT2.hotThrottlingThresholds = new float[]{};
+ halT2.coldThrottlingThresholds = new float[]{3, 2, 1};
Mockito.when(mAidlHalMock.getTemperatureThresholdsWithType(Mockito.anyInt())).thenReturn(
new TemperatureThreshold[]{halT1, halT2}
);
List<TemperatureThreshold> ret = mAidlWrapper.getTemperatureThresholds(true,
- Temperature.TYPE_SOC);
+ Temperature.TYPE_SKIN);
Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatureThresholdsWithType(
- Temperature.TYPE_SOC);
+ Temperature.TYPE_SKIN);
assertEquals("Got unexpected temperature thresholds size", 1, ret.size());
TemperatureThreshold threshold = ret.get(0);
@@ -322,6 +454,24 @@
}
@Test
+ public void getTemperatureThresholds_nullResult_aidl() throws RemoteException {
+ Mockito.when(mAidlHalMock.getTemperatureThresholdsWithType(Mockito.anyInt())).thenReturn(
+ null);
+ List<TemperatureThreshold> ret = mAidlWrapper.getTemperatureThresholds(true,
+ Temperature.TYPE_SOC);
+ Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatureThresholdsWithType(
+ Temperature.TYPE_SOC);
+ assertNotNull(ret);
+ assertEquals(0, ret.size());
+
+ Mockito.when(mAidlHalMock.getTemperatureThresholds()).thenReturn(null);
+ ret = mAidlWrapper.getTemperatureThresholds(false, Temperature.TYPE_SOC);
+ Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatureThresholds();
+ assertNotNull(ret);
+ assertEquals(0, ret.size());
+ }
+
+ @Test
public void getTemperatureThresholds_illegalArgument_aidl() throws RemoteException {
Mockito.when(mAidlHalMock.getTemperatureThresholdsWithType(Mockito.anyInt())).thenThrow(
new IllegalArgumentException());