diff options
48 files changed, 1174 insertions, 347 deletions
diff --git a/api/current.txt b/api/current.txt index b3618601e1be..e2ea649e2242 100644 --- a/api/current.txt +++ b/api/current.txt @@ -25476,6 +25476,7 @@ package android.media { method public java.lang.Object setAuxEffectSendLevel(float); method public java.lang.Object setDataSource(android.media.DataSourceDesc); method public java.lang.Object setDisplay(android.view.SurfaceHolder); + method public void setDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback); method public java.lang.Object setNextDataSource(android.media.DataSourceDesc); method public java.lang.Object setNextDataSources(java.util.List<android.media.DataSourceDesc>); method public java.lang.Object setPlaybackParams(android.media.PlaybackParams); @@ -25554,6 +25555,33 @@ package android.media { field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0 } + public static class MediaPlayer2.DrmEventCallback { + ctor public MediaPlayer2.DrmEventCallback(); + method public void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm); + method public android.media.MediaPlayer2.DrmPreparationInfo onDrmInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaPlayer2.DrmInfo); + method public byte[] onDrmKeyRequest(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm.KeyRequest); + method public void onDrmPrepared(android.media.MediaPlayer2, android.media.DataSourceDesc, int, byte[]); + } + + public static final class MediaPlayer2.DrmInfo { + method public java.util.Map<java.util.UUID, byte[]> getPssh(); + method public java.util.List<java.util.UUID> getSupportedSchemes(); + } + + public static final class MediaPlayer2.DrmPreparationInfo { + } + + public static final class MediaPlayer2.DrmPreparationInfo.Builder { + ctor public MediaPlayer2.DrmPreparationInfo.Builder(); + method public android.media.MediaPlayer2.DrmPreparationInfo build(); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setInitData(byte[]); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeySetId(byte[]); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeyType(int); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setMimeType(java.lang.String); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setOptionalParameters(java.util.Map<java.lang.String, java.lang.String>); + method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setUuid(java.util.UUID); + } + public static class MediaPlayer2.EventCallback { ctor public MediaPlayer2.EventCallback(); method public void onCallCompleted(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int); @@ -25581,6 +25609,10 @@ package android.media { field public static final java.lang.String WIDTH = "android.media.mediaplayer.width"; } + public static final class MediaPlayer2.NoDrmSchemeException extends android.media.MediaDrmException { + ctor public MediaPlayer2.NoDrmSchemeException(java.lang.String); + } + public static class MediaPlayer2.TrackInfo { method public android.media.MediaFormat getFormat(); method public java.lang.String getLanguage(); diff --git a/api/system-current.txt b/api/system-current.txt index 5fac1ee13e2c..276be9dd746d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4864,6 +4864,7 @@ package android.permission { method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean); method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String); method public abstract void onGetRuntimePermissionsBackup(android.os.UserHandle, java.io.OutputStream); + method public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onPermissionUsageResult(boolean, long); method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String); method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.lang.String); field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; @@ -4892,11 +4893,12 @@ package android.permission { public final class RuntimePermissionUsageInfo implements android.os.Parcelable { ctor public RuntimePermissionUsageInfo(java.lang.CharSequence, int); method public int describeContents(); - method public java.lang.CharSequence getName(); method public int getAppAccessCount(); + method public java.lang.CharSequence getName(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionUsageInfo> CREATOR; } + } package android.permissionpresenterservice { @@ -7252,7 +7254,6 @@ package android.telephony.ims { method public void callSessionInviteParticipantsRequestDelivered(); method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionMayHandover(int, int); - method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile); method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase); method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile); @@ -7263,6 +7264,7 @@ package android.telephony.ims { method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile); method public void callSessionResumed(android.telephony.ims.ImsCallProfile); + method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile); method public void callSessionRttMessageReceived(java.lang.String); method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile); method public void callSessionRttModifyResponseReceived(int); diff --git a/api/test-current.txt b/api/test-current.txt index fb50fa194a92..71f02b9bdc9a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -945,6 +945,11 @@ package android.os { field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR; } + public final class MessageQueue { + method public int postSyncBarrier(); + method public void removeSyncBarrier(int); + } + public final class NativeHandle implements java.io.Closeable { ctor public NativeHandle(); ctor public NativeHandle(java.io.FileDescriptor, boolean); @@ -957,11 +962,6 @@ package android.os { method public boolean hasSingleFileDescriptor(); } - public final class MessageQueue { - method public int postSyncBarrier(); - method public void removeSyncBarrier(int); - } - public final class PowerManager { method public int getPowerSaveMode(); method public boolean setDynamicPowerSavings(boolean, int); diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 57132a72700d..ab8f234766d6 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -16,6 +16,10 @@ package android.app; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; + import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager.StackInfo; @@ -32,7 +36,6 @@ import android.util.Log; import android.view.IWindowManager; import android.view.InputDevice; import android.view.MotionEvent; -import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceSession; @@ -82,7 +85,6 @@ public class ActivityView extends ViewGroup { private boolean mOpened; // Protected by mGuard. private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction(); - private Surface mTmpSurface = new Surface(); /** The ActivityView is only allowed to contain one task. */ private final boolean mSingleTaskInstance; @@ -319,20 +321,20 @@ public class ActivityView extends ViewGroup { private class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { - mTmpSurface = new Surface(); if (mVirtualDisplay == null) { initVirtualDisplay(new SurfaceSession()); if (mVirtualDisplay != null && mActivityViewCallback != null) { mActivityViewCallback.onActivityViewReady(ActivityView.this); } } else { - // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by - // whether it has a surface. Setting a fake surface here so DisplayManager will - // consider this display on. - mVirtualDisplay.setSurface(mTmpSurface); mTmpTransaction.reparent(mRootSurfaceControl, mSurfaceView.getSurfaceControl().getHandle()).apply(); } + + if (mVirtualDisplay != null) { + mVirtualDisplay.setDisplayState(true); + } + updateLocation(); } @@ -346,10 +348,8 @@ public class ActivityView extends ViewGroup { @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { - mTmpSurface.release(); - mTmpSurface = null; if (mVirtualDisplay != null) { - mVirtualDisplay.setSurface(null); + mVirtualDisplay.setDisplayState(false); } cleanTapExcludeRegion(); } @@ -370,15 +370,11 @@ public class ActivityView extends ViewGroup { final int height = mSurfaceView.getHeight(); final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); - // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by - // whether it has a surface. Setting a fake surface here so DisplayManager will consider - // this display on. mVirtualDisplay = displayManager.createVirtualDisplay( - DISPLAY_NAME + "@" + System.identityHashCode(this), - width, height, getBaseDisplayDensity(), mTmpSurface, - DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC - | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY - | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL); + DISPLAY_NAME + "@" + System.identityHashCode(this), width, height, + getBaseDisplayDensity(), null, + VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + | VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL); if (mVirtualDisplay == null) { Log.e(TAG, "Failed to initialize ActivityView"); return; @@ -443,11 +439,6 @@ public class ActivityView extends ViewGroup { displayReleased = false; } - if (mTmpSurface != null) { - mTmpSurface.release(); - mTmpSurface = null; - } - if (displayReleased && mActivityViewCallback != null) { mActivityViewCallback.onActivityViewDestroyed(this); } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index cda8498c4f01..7e45441c804a 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -436,6 +436,7 @@ public final class DisplayManagerGlobal { public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) { try { mDm.setVirtualDisplaySurface(token, surface); + setVirtualDisplayState(token, surface != null); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -458,6 +459,14 @@ public final class DisplayManagerGlobal { } } + void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) { + try { + mDm.setVirtualDisplayState(token, isOn); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + /** * Gets the stable device display size, in pixels. */ diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 2d81cdfec179..aae8afbcad49 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -82,6 +82,9 @@ interface IDisplayManager { // No permissions required but must be same Uid as the creator. void releaseVirtualDisplay(in IVirtualDisplayCallback token); + // No permissions required but must be same Uid as the creator. + void setVirtualDisplayState(in IVirtualDisplayCallback token, boolean isOn); + // Get a stable metric for the device's display size. No permissions required. Point getStableDisplaySize(); diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java index d35466605162..bf62c95cf6db 100644 --- a/core/java/android/hardware/display/VirtualDisplay.java +++ b/core/java/android/hardware/display/VirtualDisplay.java @@ -104,6 +104,18 @@ public final class VirtualDisplay { } } + /** + * Sets the on/off state for a virtual display. + * + * @param isOn Whether the display should be on or off. + * @hide + */ + public void setDisplayState(boolean isOn) { + if (mToken != null) { + mGlobal.setVirtualDisplayState(mToken, isOn); + } + } + @Override public String toString() { return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java index d382eb9310d9..bdd5ab67af48 100644 --- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java +++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java @@ -15,10 +15,12 @@ */ package android.hardware.hdmi; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; @@ -56,6 +58,21 @@ public final class HdmiAudioSystemClient extends HdmiClient { mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler; } + /** + * Callback interface used to get the set System Audio Mode result. + * + * @hide + */ + // TODO(b/110094868): unhide and add @SystemApi for Q + public interface SetSystemAudioModeCallback { + /** + * Called when the input was changed. + * + * @param result the result of the set System Audio Mode + */ + void onComplete(int result); + } + /** @hide */ // TODO(b/110094868): unhide and add @SystemApi for Q @Override @@ -117,4 +134,34 @@ public final class HdmiAudioSystemClient extends HdmiClient { mPendingReportAudioStatus = true; } } + + /** + * Set System Audio Mode on/off with audio system device. + * + * @param state true to set System Audio Mode on. False to set off. + * @param callback callback offer the setting result. + * + * @hide + */ + // TODO(b/110094868): unhide and add @SystemApi for Q + public void setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback) { + // TODO(amyjojo): implement this when needed. + } + + /** + * When device is switching to an audio only source, this method is called to broadcast + * a setSystemAudioMode on message to the HDMI CEC system without querying Active Source or + * TV supporting System Audio Control or not. This is to get volume control passthrough + * from STB even if TV does not support it. + * + * @hide + */ + // TODO(b/110094868): unhide and add @SystemApi for Q + public void setSystemAudioModeOnForAudioOnlySource() { + try { + mService.setSystemAudioModeOnForAudioOnlySource(); + } catch (RemoteException e) { + Log.d(TAG, "Failed to set System Audio Mode on for Audio Only source"); + } + } } diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index f5d288e6a233..be8009e6a966 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -432,6 +432,19 @@ public final class HdmiControlManager { } /** + * Get the physical address of the device. + * + * @hide + */ + public int getPhysicalAddress() { + try { + return mService.getPhysicalAddress(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Listener used to get hotplug event from HDMI port. */ public interface HotplugEventListener { diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 2b8d00b73b7e..66bb084d5482 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -50,6 +50,7 @@ interface IHdmiControlService { List<HdmiPortInfo> getPortInfo(); boolean canChangeSystemAudioMode(); boolean getSystemAudioMode(); + int getPhysicalAddress(); void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback); void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener); void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener); @@ -73,4 +74,5 @@ interface IHdmiControlService { void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener); void setStandbyMode(boolean isStandbyModeOn); void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute); + void setSystemAudioModeOnForAudioOnlySource(); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 84825484c36d..10555fab0a8a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9381,6 +9381,15 @@ public final class Settings { "hdmi_system_audio_control_enabled"; /** + * Whether HDMI Routing Control feature is enabled. If enabled, the switch device will + * route to the correct input source on receiving Routing Control related messages. If + * disabled, you can only switch the input via controls on this device. + * @hide + */ + public static final String HDMI_CEC_SWITCH_ENABLED = + "hdmi_cec_switch_enabled"; + + /** * Whether TV will automatically turn on upon reception of the CEC command * <Text View On> or <Image View On>. (0 = false, 1 = true) * diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 1ed5ce4a3929..341f345ce11f 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -270,6 +270,7 @@ public class SettingsBackupTest { Settings.Global.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS, Settings.Global.GNSS_SATELLITE_BLACKLIST, Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS, + Settings.Global.HDMI_CEC_SWITCH_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, Settings.Global.HDMI_CONTROL_ENABLED, diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java index 0dd9d0904746..64b3ba04e841 100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java @@ -318,6 +318,15 @@ public class HdmiAudioSystemClientTest { mMaxVolume = maxVolume; mIsMute = isMute; } + + @Override + public void setSystemAudioModeOnForAudioOnlySource() { + } + + @Override + public int getPhysicalAddress() { + return 0x0000; + } } } diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp index 9170d6d1dc50..68541b4b31f0 100644 --- a/libs/hwui/WebViewFunctorManager.cpp +++ b/libs/hwui/WebViewFunctorManager.cpp @@ -86,6 +86,26 @@ void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) { mCallbacks.gles.draw(mFunctor, mData, drawInfo); } +void WebViewFunctor::initVk(const VkFunctorInitParams& params) { + ATRACE_NAME("WebViewFunctor::initVk"); + if (!mHasContext) { + mHasContext = true; + } else { + return; + } + mCallbacks.vk.initialize(mFunctor, mData, params); +} + +void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) { + ATRACE_NAME("WebViewFunctor::drawVk"); + mCallbacks.vk.draw(mFunctor, mData, params); +} + +void WebViewFunctor::postDrawVk() { + ATRACE_NAME("WebViewFunctor::postDrawVk"); + mCallbacks.vk.postDraw(mFunctor, mData); +} + void WebViewFunctor::destroyContext() { if (mHasContext) { mHasContext = false; diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h index 1719ce7cca75..2846cb1f087b 100644 --- a/libs/hwui/WebViewFunctorManager.h +++ b/libs/hwui/WebViewFunctorManager.h @@ -42,6 +42,12 @@ public: void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); } + void initVk(const VkFunctorInitParams& params) { mReference.initVk(params); } + + void drawVk(const VkFunctorDrawParams& params) { mReference.drawVk(params); } + + void postDrawVk() { mReference.postDrawVk(); } + private: friend class WebViewFunctor; @@ -53,6 +59,9 @@ public: int id() const { return mFunctor; } void sync(const WebViewSyncData& syncData) const; void drawGl(const DrawGlInfo& drawInfo); + void initVk(const VkFunctorInitParams& params); + void drawVk(const VkFunctorDrawParams& params); + void postDrawVk(); void destroyContext(); sp<Handle> createHandle() { diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 6eefed959913..d54275fe7e19 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -125,8 +125,6 @@ void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, uirenderer::GlFunctorLifecycleListener* listener) { FunctorDrawable* functorDrawable; if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the - // interop is disabled/moved. functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>( functor, listener, asSkCanvas()); } else { diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp index 156f74a611a7..2f8d381f15f5 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -17,6 +17,8 @@ #include "VkFunctorDrawable.h" #include <private/hwui/DrawVkInfo.h> +#include "renderthread/VulkanManager.h" +#include "renderthread/RenderThread.h" #include <GrBackendDrawableInfo.h> #include <SkImage.h> #include <utils/Color.h> @@ -31,34 +33,58 @@ namespace android { namespace uirenderer { namespace skiapipeline { -VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {} +VkFunctorDrawHandler::VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_handle, + const SkMatrix& matrix, const SkIRect& clip, + const SkImageInfo& image_info) + : INHERITED() + , mFunctorHandle(functor_handle) + , mMatrix(matrix) + , mClip(clip) + , mImageInfo(image_info) {} VkFunctorDrawHandler::~VkFunctorDrawHandler() { - // TODO(cblume) Fill in the DrawVkInfo parameters. - (*mFunctor)(DrawVkInfo::kModePostComposite, nullptr); + mFunctorHandle->postDrawVk(); } void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { ATRACE_CALL(); + if (!renderthread::RenderThread::isCurrent()) + LOG_ALWAYS_FATAL("VkFunctorDrawHandler::draw not called on render thread"); GrVkDrawableInfo vulkan_info; if (!info.getVkDrawableInfo(&vulkan_info)) { return; } + renderthread::VulkanManager& vk_manager = + renderthread::RenderThread::getInstance().vulkanManager(); + mFunctorHandle->initVk(vk_manager.getVkFunctorInitParams()); - DrawVkInfo draw_vk_info; - // TODO(cblume) Fill in the rest of the parameters and test the actual call. - draw_vk_info.isLayer = true; + SkMatrix44 mat4(mMatrix); + VkFunctorDrawParams params{ + .width = mImageInfo.width(), + .height = mImageInfo.height(), + .is_layer = false, // TODO(boliu): Populate is_layer. + .color_space_ptr = mImageInfo.colorSpace(), + .clip_left = mClip.fLeft, + .clip_top = mClip.fTop, + .clip_right = mClip.fRight, + .clip_bottom = mClip.fBottom, + }; + mat4.asColMajorf(¶ms.transform[0]); + params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer; + params.color_attachment_index = vulkan_info.fColorAttachmentIndex; + params.compatible_render_pass = vulkan_info.fCompatibleRenderPass; + params.format = vulkan_info.fFormat; - (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info); + mFunctorHandle->drawVk(params); + + vulkan_info.fDrawBounds->offset.x = mClip.fLeft; + vulkan_info.fDrawBounds->offset.y = mClip.fTop; + vulkan_info.fDrawBounds->extent.width = mClip.fRight - mClip.fLeft; + vulkan_info.fDrawBounds->extent.height = mClip.fBottom - mClip.fTop; } VkFunctorDrawable::~VkFunctorDrawable() { - if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) { - if (lp->listener) { - lp->listener->onGlFunctorReleased(lp->functor); - } - } } void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) { @@ -67,16 +93,17 @@ void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) { } std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler( - GrBackendApi backendApi, const SkMatrix& matrix) { + GrBackendApi backendApi, const SkMatrix& matrix, const SkIRect& clip, + const SkImageInfo& image_info) { if (backendApi != GrBackendApi::kVulkan) { return nullptr; } std::unique_ptr<VkFunctorDrawHandler> draw; if (mAnyFunctor.index() == 0) { - LOG_ALWAYS_FATAL("Not implemented"); - return nullptr; + return std::make_unique<VkFunctorDrawHandler>(std::get<0>(mAnyFunctor).handle, matrix, clip, + image_info); } else { - return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor); + LOG_ALWAYS_FATAL("Not implemented"); } } diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h index d6fefc1fca06..1a53c8fd55db 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h @@ -32,15 +32,18 @@ namespace skiapipeline { */ class VkFunctorDrawHandler : public FunctorDrawable::GpuDrawHandler { public: - explicit VkFunctorDrawHandler(Functor* functor); + VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_handle, const SkMatrix& matrix, + const SkIRect& clip, const SkImageInfo& image_info); ~VkFunctorDrawHandler() override; void draw(const GrBackendDrawableInfo& info) override; private: typedef GpuDrawHandler INHERITED; - - Functor* mFunctor; + sp<WebViewFunctor::Handle> mFunctorHandle; + const SkMatrix mMatrix; + const SkIRect mClip; + const SkImageInfo mImageInfo; }; /** @@ -57,7 +60,8 @@ protected: // SkDrawable functions: void onDraw(SkCanvas* canvas) override; std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler( - GrBackendApi backendApi, const SkMatrix& matrix) override; + GrBackendApi backendApi, const SkMatrix& matrix, const SkIRect& clip, + const SkImageInfo& image_info) override; }; } // namespace skiapipeline diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h index fd824bd957fd..abc4dbf9fa1c 100644 --- a/libs/hwui/private/hwui/DrawVkInfo.h +++ b/libs/hwui/private/hwui/DrawVkInfo.h @@ -17,80 +17,61 @@ #ifndef ANDROID_HWUI_DRAW_VK_INFO_H #define ANDROID_HWUI_DRAW_VK_INFO_H +#include <SkColorSpace.h> #include <vulkan/vulkan.h> namespace android { namespace uirenderer { -/** - * Structure used by VulkanRenderer::callDrawVKFunction() to pass and receive data from Vulkan - * functors. - */ -struct DrawVkInfo { - // Input: current width/height of destination surface - int width; - int height; - - // Input: is the render target an FBO - bool isLayer; - - // Input: current transform matrix, in OpenGL format - float transform[16]; - - // Input: WebView should do its main compositing draws into this. It cannot do anything that - // would require stopping the render pass. - VkCommandBuffer secondaryCommandBuffer; - - // Input: The main color attachment index where secondaryCommandBuffer will eventually be - // submitted. - uint32_t colorAttachmentIndex; - - // Input: A render pass which will be compatible to the one which the secondaryCommandBuffer - // will be submitted into. - VkRenderPass compatibleRenderPass; - - // Input: Format of the destination surface. - VkFormat format; - - // Input: Color space - const SkColorSpace* colorSpaceInfo; - - // Input: current clip rect - int clipLeft; - int clipTop; - int clipRight; - int clipBottom; - - /** - * Values used as the "what" parameter of the functor. - */ - enum Mode { - // Called once at WebView start - kModeInit, - // Called when things need to be re-created - kModeReInit, - // Notifies the app that the composite functor will be called soon. This allows WebView to - // begin work early. - kModePreComposite, - // Do the actual composite work - kModeComposite, - // This allows WebView to begin using the previously submitted objects in future work. - kModePostComposite, - // Invoked every time the UI thread pushes over a frame to the render thread and the owning - // view has a dirty display list*. This is a signal to sync any data that needs to be - // shared between the UI thread and the render thread. During this time the UI thread is - // blocked. - kModeSync - }; - - /** - * Values used by Vulkan functors to tell the framework what to do next. - */ - enum Status { - // The functor is done - kStatusDone = 0x0, - }; -}; // struct DrawVkInfo +struct VkFunctorInitParams { + VkInstance instance; + VkPhysicalDevice physical_device; + VkDevice device; + VkQueue queue; + uint32_t graphics_queue_index; + uint32_t instance_version; + const char* const* enabled_instance_extension_names; + uint32_t enabled_instance_extension_names_length; + const char* const* enabled_device_extension_names; + uint32_t enabled_device_extension_names_length; + const VkPhysicalDeviceFeatures2* device_features_2; +}; + +struct VkFunctorDrawParams { + // Input: current width/height of destination surface. + int width; + int height; + + // Input: is the render target a FBO + bool is_layer; + + // Input: current transform matrix + float transform[16]; + + // Input WebView should do its main compositing draws into this. It cannot do + // anything that would require stopping the render pass. + VkCommandBuffer secondary_command_buffer; + + // Input: The main color attachment index where secondary_command_buffer will + // eventually be submitted. + uint32_t color_attachment_index; + + // Input: A render pass which will be compatible to the one which the + // secondary_command_buffer will be submitted into. + VkRenderPass compatible_render_pass; + + // Input: Format of the destination surface. + VkFormat format; + + // Input: Color space. + const SkColorSpace* color_space_ptr; + + // Input: current clip rect + int clip_left; + int clip_top; + int clip_right; + int clip_bottom; +}; } // namespace uirenderer } // namespace android diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h index da3d06a4d60c..96da947ace08 100644 --- a/libs/hwui/private/hwui/WebViewFunctor.h +++ b/libs/hwui/private/hwui/WebViewFunctor.h @@ -19,6 +19,7 @@ #include <cutils/compiler.h> #include <private/hwui/DrawGlInfo.h> +#include <private/hwui/DrawVkInfo.h> namespace android::uirenderer { @@ -52,18 +53,12 @@ struct WebViewFunctorCallbacks { // Called on RenderThread. initialize is guaranteed to happen before this call void (*draw)(int functor, void* data, const DrawGlInfo& params); } gles; - // TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for - // what params are valid on what callbacks struct { // Called either the first time the functor is used or the first time it's used after // a call to onContextDestroyed. - // void (*initialize)(int functor, const InitParams& params); - // void (*frameStart)(int functor, /* todo: what params are actually needed for this to - // be useful? Is this useful? */) - // void (*draw)(int functor, const CompositeParams& params /* todo: rename - composite - // almost always means something else, and we aren't compositing */); - // void (*frameEnd)(int functor, const PostCompositeParams& params /* todo: same as - // CompositeParams - rename */); + void (*initialize)(int functor, void* data, const VkFunctorInitParams& params); + void (*draw)(int functor, void* data, const VkFunctorDrawParams& params); + void (*postDraw)(int functor, void*); } vk; }; }; diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 5272227509c8..1ef83fb26c14 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -47,6 +47,10 @@ class Readback; class RenderState; class TestUtils; +namespace skiapipeline { +class VkFunctorDrawHandler; +} + namespace renderthread { class CanvasContext; @@ -124,6 +128,7 @@ private: friend class DummyVsyncSource; friend class android::uirenderer::TestUtils; friend class android::uirenderer::WebViewFunctor; + friend class android::uirenderer::skiapipeline::VkFunctorDrawHandler; RenderThread(); virtual ~RenderThread(); diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index aa7a141f6da3..6c540f627a68 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -34,6 +34,23 @@ namespace android { namespace uirenderer { namespace renderthread { +static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) { + // All Vulkan structs that could be part of the features chain will start with the + // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader + // so we can get access to the pNext for the next struct. + struct CommonVulkanHeader { + VkStructureType sType; + void* pNext; + }; + + void* pNext = features.pNext; + while (pNext) { + void* current = pNext; + pNext = static_cast<CommonVulkanHeader*>(current)->pNext; + free(current); + } +} + #define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F) #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F) #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F) @@ -66,6 +83,11 @@ void VulkanManager::destroy() { mDevice = VK_NULL_HANDLE; mPhysicalDevice = VK_NULL_HANDLE; mInstance = VK_NULL_HANDLE; + mInstanceVersion = 0u; + mInstanceExtensions.clear(); + mDeviceExtensions.clear(); + free_features_extensions_structs(mPhysicalDeviceFeatures2); + mPhysicalDeviceFeatures2 = {}; } bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) { @@ -81,7 +103,6 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe VK_MAKE_VERSION(1, 1, 0), // apiVersion }; - std::vector<const char*> instanceExtensions; { GET_PROC(EnumerateInstanceExtensionProperties); @@ -99,7 +120,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe bool hasKHRSurfaceExtension = false; bool hasKHRAndroidSurfaceExtension = false; for (uint32_t i = 0; i < extensionCount; ++i) { - instanceExtensions.push_back(extensions[i].extensionName); + mInstanceExtensions.push_back(extensions[i].extensionName); if (!strcmp(extensions[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) { hasKHRSurfaceExtension = true; } @@ -120,8 +141,8 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe &app_info, // pApplicationInfo 0, // enabledLayerNameCount nullptr, // ppEnabledLayerNames - (uint32_t) instanceExtensions.size(), // enabledExtensionNameCount - instanceExtensions.data(), // ppEnabledExtensionNames + (uint32_t) mInstanceExtensions.size(), // enabledExtensionNameCount + mInstanceExtensions.data(), // ppEnabledExtensionNames }; GET_PROC(CreateInstance); @@ -201,7 +222,6 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe // presentation with any native window. So just use the first one. mPresentQueueIndex = 0; - std::vector<const char*> deviceExtensions; { uint32_t extensionCount = 0; err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount, @@ -220,7 +240,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe } bool hasKHRSwapchainExtension = false; for (uint32_t i = 0; i < extensionCount; ++i) { - deviceExtensions.push_back(extensions[i].extensionName); + mDeviceExtensions.push_back(extensions[i].extensionName); if (!strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) { hasKHRSwapchainExtension = true; } @@ -237,8 +257,8 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe } return vkGetInstanceProcAddr(instance, proc_name); }; - grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(), - instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data()); + grExtensions.init(getProc, mInstance, mPhysicalDevice, mInstanceExtensions.size(), + mInstanceExtensions.data(), mDeviceExtensions.size(), mDeviceExtensions.data()); if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { this->destroy(); @@ -308,8 +328,8 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe queueInfo, // pQueueCreateInfos 0, // layerCount nullptr, // ppEnabledLayerNames - (uint32_t) deviceExtensions.size(), // extensionCount - deviceExtensions.data(), // ppEnabledExtensionNames + (uint32_t) mDeviceExtensions.size(), // extensionCount + mDeviceExtensions.data(), // ppEnabledExtensionNames nullptr, // ppEnabledFeatures }; @@ -351,36 +371,17 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe return true; } -static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) { - // All Vulkan structs that could be part of the features chain will start with the - // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader - // so we can get access to the pNext for the next struct. - struct CommonVulkanHeader { - VkStructureType sType; - void* pNext; - }; - - void* pNext = features.pNext; - while (pNext) { - void* current = pNext; - pNext = static_cast<CommonVulkanHeader*>(current)->pNext; - free(current); - } -} - void VulkanManager::initialize() { if (mDevice != VK_NULL_HANDLE) { return; } GET_PROC(EnumerateInstanceVersion); - uint32_t instanceVersion = 0; - LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion)); - LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0)); + LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&mInstanceVersion)); + LOG_ALWAYS_FATAL_IF(mInstanceVersion < VK_MAKE_VERSION(1, 1, 0)); GrVkExtensions extensions; - VkPhysicalDeviceFeatures2 features; - LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, features)); + LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, mPhysicalDeviceFeatures2)); mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); @@ -397,9 +398,9 @@ void VulkanManager::initialize() { backendContext.fDevice = mDevice; backendContext.fQueue = mGraphicsQueue; backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex; - backendContext.fInstanceVersion = instanceVersion; + backendContext.fInstanceVersion = mInstanceVersion; backendContext.fVkExtensions = &extensions; - backendContext.fDeviceFeatures2 = &features; + backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2; backendContext.fGetProc = std::move(getProc); // create the command pool for the command buffers @@ -433,13 +434,29 @@ void VulkanManager::initialize() { LOG_ALWAYS_FATAL_IF(!grContext.get()); mRenderThread.setGrContext(grContext); - free_features_extensions_structs(features); - if (Properties::enablePartialUpdates && Properties::useBufferAge) { mSwapBehavior = SwapBehavior::BufferAge; } } +VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const { + return VkFunctorInitParams{ + .instance = mInstance, + .physical_device = mPhysicalDevice, + .device = mDevice, + .queue = mGraphicsQueue, + .graphics_queue_index = mGraphicsQueueIndex, + .instance_version = mInstanceVersion, + .enabled_instance_extension_names = mInstanceExtensions.data(), + .enabled_instance_extension_names_length = + static_cast<uint32_t>(mInstanceExtensions.size()), + .enabled_device_extension_names = mDeviceExtensions.data(), + .enabled_device_extension_names_length = + static_cast<uint32_t>(mDeviceExtensions.size()), + .device_features_2 = &mPhysicalDeviceFeatures2, + }; +} + // Returns the next BackbufferInfo to use for the next draw. The function will make sure all // previous uses have finished before returning. VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) { diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 9eb942c9d6ee..105ee093a057 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -132,6 +132,9 @@ public: // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. status_t createReleaseFence(sp<Fence>& nativeFence); + // Returned pointers are owned by VulkanManager. + VkFunctorInitParams getVkFunctorInitParams() const; + private: friend class RenderThread; @@ -234,6 +237,12 @@ private: VkCommandBuffer mDummyCB = VK_NULL_HANDLE; + // Variables saved to populate VkFunctorInitParams. + uint32_t mInstanceVersion = 0u; + std::vector<const char*> mInstanceExtensions; + std::vector<const char*> mDeviceExtensions; + VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures2{}; + enum class SwapBehavior { Discard, BufferAge, diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index 793aa270e2fd..55160869ad0a 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; @@ -816,7 +817,7 @@ public final class AudioFormat implements Parcelable { * * @return The audio frame size in bytes corresponding to the encoding and the channel mask. */ - public int getFrameSizeInBytes() { + public @IntRange(from = 1) int getFrameSizeInBytes() { return mFrameSizeInBytes; } diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index df96994b0c36..aa79c417922c 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -60,6 +60,7 @@ import java.net.URL; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -67,6 +68,7 @@ import java.util.List; import java.util.Map; import java.util.Queue; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -399,8 +401,7 @@ public class MediaPlayer2 implements AutoCloseable clearSourceInfos(); // Modular DRM clean up - mOnDrmConfigHelper = null; - synchronized (mDrmEventCbLock) { + synchronized (mDrmEventCallbackLock) { mDrmEventCallbackRecords.clear(); } @@ -775,7 +776,7 @@ public class MediaPlayer2 implements AutoCloseable } boolean hasError = false; for (DataSourceDesc dsd : dsds) { - if (dsd != null) { + if (dsd == null) { hasError = true; continue; } @@ -2889,7 +2890,7 @@ public class MediaPlayer2 implements AutoCloseable } private void sendDrmEvent(final DrmEventNotifier notifier) { - synchronized (mDrmEventCbLock) { + synchronized (mDrmEventCallbackLock) { try { for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) { cb.first.execute(() -> notifier.notify(cb.second)); @@ -2901,12 +2902,28 @@ public class MediaPlayer2 implements AutoCloseable } } + private <T> T sendDrmEventWait(final DrmEventNotifier<T> notifier) + throws InterruptedException, ExecutionException { + synchronized (mDrmEventCallbackLock) { + mDrmEventCallbackRecords.get(0); + for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) { + CompletableFuture<T> ret = new CompletableFuture<>(); + cb.first.execute(() -> ret.complete(notifier.notifyWait(cb.second))); + return ret.get(); + } + } + return null; + } + private interface EventNotifier { void notify(EventCallback callback); } - private interface DrmEventNotifier { - void notify(DrmEventCallback callback); + private interface DrmEventNotifier<T> { + default void notify(DrmEventCallback callback) { } + default T notifyWait(DrmEventCallback callback) { + return null; + } } /* Do not change these values without updating their counterparts @@ -3351,73 +3368,209 @@ public class MediaPlayer2 implements AutoCloseable // Modular DRM begin /** - * Interface definition of a callback to be invoked when the app - * can do DRM configuration (get/set properties) before the session - * is opened. This facilitates configuration of the properties, like - * 'securityLevel', which has to be set after DRM scheme creation but - * before the DRM session is opened. + * An immutable structure per {@link DataSourceDesc} with settings required to initiate a DRM + * protected playback session. * - * The only allowed DRM calls in this listener are - * {@link MediaPlayer2#getDrmPropertyString(DataSourceDesc, String)} - * and {@link MediaPlayer2#setDrmPropertyString(DataSourceDesc, String, String)}. - * @hide + * @see DrmPreparationInfo.Builder */ - public interface OnDrmConfigHelper { + public static final class DrmPreparationInfo { + /** - * Called to give the app the opportunity to configure DRM before the session is created - * - * @param mp the {@code MediaPlayer2} associated with this callback - * @param dsd the DataSourceDesc of this data source + * Mutable builder to create a {@link MediaPlayer2.DrmPreparationInfo} object. */ - public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc dsd); - } + public static final class Builder { + + private UUID mUUID; + private byte[] mKeySetId; + private byte[] mInitData; + private String mMimeType; + private int mKeyType; + private Map<String, String> mOptionalParameters; + + /** + * Set UUID of the crypto scheme selected to decrypt content. An UUID can be retrieved from + * the source listening to {@link MediaPlayer2.DrmEventCallback#onDrmInfo}. + * + * @param uuid of selected crypto scheme + * @return this + */ + public Builder setUuid(@NonNull UUID uuid) { + this.mUUID = uuid; + return this; + } - /** - * Register a callback to be invoked for configuration of the DRM object before - * the session is created. - * The callback will be invoked synchronously during the execution - * of {@link #prepareDrm}. - * - * @param listener the callback that will be run - * @hide - */ - // This is a synchronous call. - public void setOnDrmConfigHelper(OnDrmConfigHelper listener) { - mOnDrmConfigHelper = listener; - } + /** + * Set identifier of a persisted offline key obtained from + * {@link MediaPlayer2.DrmEventCallback#onDrmPrepared(MediaPlayer2, DataSourceDesc, int, byte[])}. + * + * A {@code keySetId} can be used to restore persisted offline keys into a new playback + * session of a DRM protected data source. When {@code keySetId} is set, {@code initData}, + * {@code mimeType}, {@code keyType}, {@code optionalParameters} are ignored. + * + * @param keySetId identifier of a persisted offline key + * @return this + */ + public Builder setKeySetId(@Nullable byte[] keySetId) { + this.mKeySetId = keySetId; + return this; + } + + /** + * Set container-specific DRM initialization data. Its meaning is interpreted based on + * {@code mimeType}. For example, it could contain the content ID, key ID or other data + * obtained from the content metadata that is required to generate a + * {@link MediaDrm.KeyRequest}. + * + * @param initData container-specific DRM initialization data + * @return this + */ + public Builder setInitData(@Nullable byte[] initData) { + this.mInitData = initData; + return this; + } + + /** + * Set mime type of the content + * + * @param mimeType mime type to the content + * @return this + */ + public Builder setMimeType(@Nullable String mimeType) { + this.mMimeType = mimeType; + return this; + } + + /** + * Set type of the key request. The request may be to acquire keys + * for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content, + * {@link MediaDrm#KEY_TYPE_OFFLINE}. Releasing previously acquired keys + * ({@link MediaDrm#KEY_TYPE_RELEASE}) is not allowed. + * + * @param keyType type of the key request + * @return this + */ + public Builder setKeyType(@MediaPlayer2.MediaDrmKeyType int keyType) { + this.mKeyType = keyType; + return this; + } + + /** + * Set optional parameters to be included in a {@link MediaDrm.KeyRequest} message sent to + * the license server. + * + * @param optionalParameters optional parameters to be included in a key request + * @return this + */ + public Builder setOptionalParameters( + @Nullable Map<String, String> optionalParameters) { + this.mOptionalParameters = optionalParameters; + return this; + } + + /** + * @return an immutable {@link MediaPlayer2.DrmPreparationInfo} representing the settings of this builder + */ + public MediaPlayer2.DrmPreparationInfo build() { + return new MediaPlayer2.DrmPreparationInfo(mUUID, mKeySetId, mInitData, mMimeType, mKeyType, + mOptionalParameters); + } + + } + + private final UUID mUUID; + private final byte[] mKeySetId; + private final byte[] mInitData; + private final String mMimeType; + private final int mKeyType; + private final Map<String, String> mOptionalParameters; - private OnDrmConfigHelper mOnDrmConfigHelper; + private DrmPreparationInfo(UUID mUUID, byte[] mKeySetId, byte[] mInitData, String mMimeType, + int mKeyType, Map<String, String> optionalParameters) { + this.mUUID = mUUID; + this.mKeySetId = mKeySetId; + this.mInitData = mInitData; + this.mMimeType = mMimeType; + this.mKeyType = mKeyType; + this.mOptionalParameters = optionalParameters; + } + + } /** * Interface definition for callbacks to be invoked when the player has the corresponding * DRM events. - * @hide */ public static class DrmEventCallback { /** - * Called to indicate DRM info is available + * Called to indicate DRM info is available. Return a {@link DrmPreparationInfo} object that + * bundles DRM initialization parameters. * * @param mp the {@code MediaPlayer2} associated with this callback - * @param dsd the DataSourceDesc of this data source - * @param drmInfo DRM info of the source including PSSH, and subset - * of crypto schemes supported by this device + * @param dsd the {@link DataSourceDesc} of this data source + * @param drmInfo DRM info of the source including PSSH, and subset of crypto schemes + * supported by this device + * @return a {@link DrmPreparationInfo} object to initialize DRM playback, or null to skip + * DRM initialization */ - public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) { } + public DrmPreparationInfo onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) { + return null; + } /** - * Called to notify the client that {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)} - * is finished and ready for key request/response. + * Called to notify the client that {@code mp} is ready to decrypt DRM protected data source + * {@code dsd} * * @param mp the {@code MediaPlayer2} associated with this callback - * @param dsd the DataSourceDesc of this data source + * @param dsd the {@link DataSourceDesc} of this data source * @param status the result of DRM preparation. + * @param keySetId optional identifier that can be used to restore DRM playback initiated + * with a {@link MediaDrm#KEY_TYPE_OFFLINE} key request. + * + * @see DrmPreparationInfo.Builder#setKeySetId(byte[]) + */ + public void onDrmPrepared(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @PrepareDrmStatusCode int status, @Nullable byte[] keySetId) { } + + /** + * Called to give the app the opportunity to configure DRM before the session is created. + * + * This facilitates configuration of the properties, like 'securityLevel', which + * has to be set after DRM scheme creation but before the DRM session is opened. + * + * The only allowed DRM calls in this listener are + * {@link MediaDrm#getPropertyString(String)}, + * {@link MediaDrm#getPropertyByteArray(String)}, + * {@link MediaDrm#setPropertyString(String, String)}, + * {@link MediaDrm#setPropertyByteArray(String, byte[])}, + * {@link MediaDrm#setOnExpirationUpdateListener}, + * and {@link MediaDrm#setOnKeyStatusChangeListener}. + * + * @param mp the {@code MediaPlayer2} associated with this callback + * @param dsd the {@link DataSourceDesc} of this data source + * @param drm handle to get/set DRM properties and listeners for this data source */ - public void onDrmPrepared( - MediaPlayer2 mp, DataSourceDesc dsd, @PrepareDrmStatusCode int status) { } + public void onDrmConfig(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @NonNull MediaDrm drm) { } + + /** + * Called to indicate the DRM session for {@code dsd} is ready for key request/response + * + * @param mp the {@code MediaPlayer2} associated with this callback + * @param dsd the {@link DataSourceDesc} of this data source + * @param request a {@link MediaDrm.KeyRequest} prepared using the + * {@link DrmPreparationInfo} returned from + * {@link #onDrmInfo(MediaPlayer2, DataSourceDesc, DrmInfo)} + * @return the response to {@code request} (from license server) + */ + public byte[] onDrmKeyRequest(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, + @NonNull MediaDrm.KeyRequest request) { + return null; + } + } - private final Object mDrmEventCbLock = new Object(); - private ArrayList<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords = + private final Object mDrmEventCallbackLock = new Object(); + private List<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords = new ArrayList<Pair<Executor, DrmEventCallback>>(); /** @@ -3425,10 +3578,9 @@ public class MediaPlayer2 implements AutoCloseable * * @param eventCallback the callback that will be run * @param executor the executor through which the callback should be invoked - * @hide */ // This is a synchronous call. - public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor, + public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor, @NonNull DrmEventCallback eventCallback) { if (eventCallback == null) { throw new IllegalArgumentException("Illegal null EventCallback"); @@ -3437,8 +3589,9 @@ public class MediaPlayer2 implements AutoCloseable throw new IllegalArgumentException( "Illegal null Executor for the EventCallback"); } - synchronized (mDrmEventCbLock) { - mDrmEventCallbackRecords.add(new Pair(executor, eventCallback)); + synchronized (mDrmEventCallbackLock) { + mDrmEventCallbackRecords = Collections.singletonList( + new Pair<Executor, DrmEventCallback>(executor, eventCallback)); } } @@ -3450,7 +3603,7 @@ public class MediaPlayer2 implements AutoCloseable */ // This is a synchronous call. public void unregisterDrmEventCallback(DrmEventCallback eventCallback) { - synchronized (mDrmEventCbLock) { + synchronized (mDrmEventCallbackLock) { for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) { if (cb.second == eventCallback) { mDrmEventCallbackRecords.remove(cb); @@ -3564,7 +3717,7 @@ public class MediaPlayer2 implements AutoCloseable /** * Prepares the DRM for the given data source * <p> - * If {@link OnDrmConfigHelper} is registered, it will be called during + * If {@link DrmEventCallback} is registered, it will be called during * preparation to allow configuration of the DRM properties before opening the * DRM session. It should be used only for a series of * {@link #getDrmPropertyString(DataSourceDesc, String)} and @@ -3587,8 +3740,7 @@ public class MediaPlayer2 implements AutoCloseable * @param dsd The DRM protected data source * * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved - * from the source through {@link #getDrmInfo(DataSourceDesc)} or registering a - * {@link DrmEventCallback#onDrmInfo}. + * from the source listening to {@link DrmEventCallback#onDrmInfo}. * * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. * @hide @@ -3661,8 +3813,8 @@ public class MediaPlayer2 implements AutoCloseable sendDrmEvent(new DrmEventNotifier() { @Override public void notify(DrmEventCallback callback) { - callback.onDrmPrepared( - MediaPlayer2.this, dsd, prepareDrmStatus); + callback.onDrmPrepared(MediaPlayer2.this, dsd, prepareDrmStatus, + /* TODO: keySetId */ null); } }); @@ -3876,7 +4028,6 @@ public class MediaPlayer2 implements AutoCloseable /** * Encapsulates the DRM properties of the source. - * @hide */ public static final class DrmInfo { private Map<UUID, byte[]> mMapPssh; @@ -4013,10 +4164,8 @@ public class MediaPlayer2 implements AutoCloseable }; // DrmInfo /** - * Thrown when a DRM method is called before preparing a DRM scheme through - * {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}. + * Thrown when a DRM method is called when there is no active DRM session. * Extends MediaDrm.MediaDrmException - * @hide */ public static final class NoDrmSchemeException extends MediaDrmException { public NoDrmSchemeException(String detailMessage) { @@ -4291,9 +4440,9 @@ public class MediaPlayer2 implements AutoCloseable } void prepare(UUID uuid) throws UnsupportedSchemeException, - ResourceBusyException, NotProvisionedException { - final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper; - Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper); + ResourceBusyException, NotProvisionedException, InterruptedException, + ExecutionException { + Log.v(TAG, "prepareDrm: uuid: " + uuid); synchronized (this) { if (mActiveDrmUUID != null) { @@ -4334,9 +4483,13 @@ public class MediaPlayer2 implements AutoCloseable } // synchronized // call the callback outside the lock - if (onDrmConfigHelper != null) { - onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD); - } + sendDrmEventWait(new DrmEventNotifier<Void>() { + @Override + public Void notifyWait(DrmEventCallback callback) { + callback.onDrmConfig(MediaPlayer2.this, mDSD, mDrmObj); + return null; + } + }); synchronized (this) { mDrmConfigAllowed = false; @@ -4506,7 +4659,7 @@ public class MediaPlayer2 implements AutoCloseable @Override public void notify(DrmEventCallback callback) { callback.onDrmPrepared( - MediaPlayer2.this, mDSD, finalStatus); + MediaPlayer2.this, mDSD, finalStatus, /* TODO: keySetId */ null); } }); diff --git a/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h index bb2ee9b5da04..0490e650a7a4 100644 --- a/native/webview/plat_support/draw_fn.h +++ b/native/webview/plat_support/draw_fn.h @@ -74,7 +74,10 @@ struct AwDrawFn_InitVkParams { VkQueue queue; uint32_t graphics_queue_index; uint32_t instance_version; - const char* const* enabled_extension_names; + const char* const* enabled_instance_extension_names; + uint32_t enabled_instance_extension_names_length; + const char* const* enabled_device_extension_names; + uint32_t enabled_device_extension_names_length; // Only one of device_features and device_features_2 should be non-null. // If both are null then no features are enabled. VkPhysicalDeviceFeatures* device_features; @@ -128,15 +131,13 @@ struct AwDrawFn_DrawVkParams { struct AwDrawFn_PostDrawVkParams { int version; - - // Input: Fence for the composite command buffer to signal it has finished its - // work on the GPU. - int fd; }; // Called on render thread while UI thread is blocked. Called for both GL and // VK. -typedef void AwDrawFn_OnSync(int functor, void* data, AwDrawFn_OnSyncParams* params); +typedef void AwDrawFn_OnSync(int functor, + void* data, + AwDrawFn_OnSyncParams* params); // Called on render thread when either the context is destroyed _or_ when the // functor's last reference goes away. Will always be called with an active @@ -150,17 +151,24 @@ typedef void AwDrawFn_OnContextDestroyed(int functor, void* data); typedef void AwDrawFn_OnDestroyed(int functor, void* data); // Only called for GL. -typedef void AwDrawFn_DrawGL(int functor, void* data, AwDrawFn_DrawGLParams* params); +typedef void AwDrawFn_DrawGL(int functor, + void* data, + AwDrawFn_DrawGLParams* params); // Initialize vulkan state. Needs to be called again after any // OnContextDestroyed. Only called for Vulkan. -typedef void AwDrawFn_InitVk(int functor, void* data, AwDrawFn_InitVkParams* params); +typedef void AwDrawFn_InitVk(int functor, + void* data, + AwDrawFn_InitVkParams* params); // Only called for Vulkan. -typedef void AwDrawFn_DrawVk(int functor, void* data, AwDrawFn_DrawVkParams* params); +typedef void AwDrawFn_DrawVk(int functor, + void* data, + AwDrawFn_DrawVkParams* params); // Only called for Vulkan. -typedef void AwDrawFn_PostDrawVk(int functor, void* data, +typedef void AwDrawFn_PostDrawVk(int functor, + void* data, AwDrawFn_PostDrawVkParams* params); struct AwDrawFnFunctorCallbacks { @@ -183,7 +191,8 @@ enum AwDrawFnRenderMode { typedef AwDrawFnRenderMode AwDrawFn_QueryRenderMode(void); // Create a functor. |functor_callbacks| should be valid until OnDestroyed. -typedef int AwDrawFn_CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks); +typedef int AwDrawFn_CreateFunctor(void* data, + AwDrawFnFunctorCallbacks* functor_callbacks); // May be called on any thread to signal that the functor should be destroyed. // The functor will receive an onDestroyed when the last usage of it is diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp index 6c1ceaba4d46..b97bbc311624 100644 --- a/native/webview/plat_support/draw_functor.cpp +++ b/native/webview/plat_support/draw_functor.cpp @@ -74,6 +74,79 @@ void draw_gl(int functor, void* data, support->callbacks.draw_gl(functor, support->data, ¶ms); } +void initializeVk(int functor, void* data, + const uirenderer::VkFunctorInitParams& init_vk_params) { + SupportData* support = static_cast<SupportData*>(data); + VkPhysicalDeviceFeatures2 device_features_2; + if (init_vk_params.device_features_2) + device_features_2 = *init_vk_params.device_features_2; + + AwDrawFn_InitVkParams params{ + .version = kAwDrawFnVersion, + .instance = init_vk_params.instance, + .physical_device = init_vk_params.physical_device, + .device = init_vk_params.device, + .queue = init_vk_params.queue, + .graphics_queue_index = init_vk_params.graphics_queue_index, + .instance_version = init_vk_params.instance_version, + .enabled_instance_extension_names = + init_vk_params.enabled_instance_extension_names, + .enabled_instance_extension_names_length = + init_vk_params.enabled_instance_extension_names_length, + .enabled_device_extension_names = + init_vk_params.enabled_device_extension_names, + .enabled_device_extension_names_length = + init_vk_params.enabled_device_extension_names_length, + .device_features = nullptr, + .device_features_2 = + init_vk_params.device_features_2 ? &device_features_2 : nullptr, + }; + support->callbacks.init_vk(functor, support->data, ¶ms); +} + +void drawVk(int functor, void* data, const uirenderer::VkFunctorDrawParams& draw_vk_params) { + SupportData* support = static_cast<SupportData*>(data); + float gabcdef[7]; + draw_vk_params.color_space_ptr->transferFn(gabcdef); + AwDrawFn_DrawVkParams params{ + .version = kAwDrawFnVersion, + .width = draw_vk_params.width, + .height = draw_vk_params.height, + .is_layer = draw_vk_params.is_layer, + .secondary_command_buffer = draw_vk_params.secondary_command_buffer, + .color_attachment_index = draw_vk_params.color_attachment_index, + .compatible_render_pass = draw_vk_params.compatible_render_pass, + .format = draw_vk_params.format, + .transfer_function_g = gabcdef[0], + .transfer_function_a = gabcdef[1], + .transfer_function_b = gabcdef[2], + .transfer_function_c = gabcdef[3], + .transfer_function_d = gabcdef[4], + .transfer_function_e = gabcdef[5], + .transfer_function_f = gabcdef[6], + .clip_left = draw_vk_params.clip_left, + .clip_top = draw_vk_params.clip_top, + .clip_right = draw_vk_params.clip_right, + .clip_bottom = draw_vk_params.clip_bottom, + }; + COMPILE_ASSERT(sizeof(params.color_space_toXYZD50) == sizeof(skcms_Matrix3x3), + gamut_transform_size_mismatch); + draw_vk_params.color_space_ptr->toXYZD50( + reinterpret_cast<skcms_Matrix3x3*>(¶ms.color_space_toXYZD50)); + COMPILE_ASSERT(NELEM(params.transform) == NELEM(draw_vk_params.transform), + mismatched_transform_matrix_sizes); + for (int i = 0; i < NELEM(params.transform); ++i) { + params.transform[i] = draw_vk_params.transform[i]; + } + support->callbacks.draw_vk(functor, support->data, ¶ms); +} + +void postDrawVk(int functor, void* data) { + SupportData* support = static_cast<SupportData*>(data); + AwDrawFn_PostDrawVkParams params{.version = kAwDrawFnVersion}; + support->callbacks.post_draw_vk(functor, support->data, ¶ms); +} + int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) { static bool callbacks_initialized = false; static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = { @@ -82,9 +155,19 @@ int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) { .onDestroyed = &onDestroyed, }; if (!callbacks_initialized) { - // Under uirenderer::RenderMode::Vulkan, whether gles or vk union should - // be populated should match whether the vk-gl interop is used. - webview_functor_callbacks.gles.draw = &draw_gl; + switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) { + case uirenderer::RenderMode::OpenGL_ES: + webview_functor_callbacks.gles.draw = &draw_gl; + break; + case uirenderer::RenderMode::Vulkan: + webview_functor_callbacks.vk.initialize = &initializeVk; + webview_functor_callbacks.vk.draw = &drawVk; + webview_functor_callbacks.vk.postDraw = &postDrawVk; + // TODO(boliu): Remove this once SkiaRecordingCanvas::drawWebViewFunctor + // no longer uses GL interop. + webview_functor_callbacks.gles.draw = &draw_gl; + break; + } callbacks_initialized = true; } SupportData* support = new SupportData{ diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index a29cf8449e51..9e89b890d583 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -103,6 +103,8 @@ <string name="ssid_by_passpoint_provider"><xliff:g id="ssid" example="Cafe Wifi">%1$s</xliff:g> by <xliff:g id="passpointProvider" example="Passpoint Provider">%2$s</xliff:g></string> <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] --> <string name="available_via_passpoint">Available via %1$s</string> + <!-- Status message of OSU Provider network when not connected. [CHAR LIMIT=NONE] --> + <string name="tap_to_set_up">Tap to set up</string> <!-- Package name for Settings app--> <string name="settings_package" translatable="false">com.android.settings</string> <!-- Package name for Certinstaller app--> diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 1ae1d56aacc8..a97931a946bf 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -926,6 +926,8 @@ public class AccessPoint implements Comparable<AccessPoint> { } else if (mIsCarrierAp) { summary.append(String.format(mContext.getString( R.string.available_via_carrier), mCarrierName)); + } else if (isOsuProvider()) { + summary.append(mContext.getString(R.string.tap_to_set_up)); } else if (!isReachable()) { // Wifi out of range summary.append(mContext.getString(R.string.wifi_not_in_range)); } else { // In range, not disabled. diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index 523720d54eec..1a684a0ffb8f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -323,7 +323,7 @@ public class PluginInstanceManager<T extends Plugin> { return null; } // Create our own ClassLoader so we can use our own code as the parent. - ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName); + ClassLoader classLoader = mManager.getClassLoader(info); Context pluginContext = new PluginContextWrapper( mContext.createApplicationContext(info, 0), classLoader); Class<?> pluginClass = Class.forName(cls, true, classLoader); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index da143f9bcbe3..7139708b65b9 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -14,6 +14,7 @@ package com.android.systemui.shared.plugins; +import android.app.LoadedApk; import android.app.Notification; import android.app.Notification.Action; import android.app.NotificationManager; @@ -44,15 +45,17 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.annotations.ProvidesInterface; -import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper; import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; import dalvik.system.PathClassLoader; +import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.Thread.UncaughtExceptionHandler; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; /** * @see Plugin @@ -117,6 +120,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage return mPluginEnabler; } + // TODO(mankoff): This appears to be only called from tests. Remove? public <T extends Plugin> T getOneShotPlugin(Class<T> cls) { ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class); if (info == null) { @@ -282,17 +286,25 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } } - public ClassLoader getClassLoader(String sourceDir, String pkg) { - if (!isDebuggable && !mWhitelistedPlugins.contains(pkg)) { - Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" + sourceDir + - ", pkg: " + pkg); + /** Returns class loader specific for the given plugin. */ + public ClassLoader getClassLoader(ApplicationInfo appInfo) { + if (!isDebuggable && !mWhitelistedPlugins.contains(appInfo.packageName)) { + Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" + + appInfo.sourceDir + ", pkg: " + appInfo.packageName); return null; } - if (mClassLoaders.containsKey(pkg)) { - return mClassLoaders.get(pkg); + if (mClassLoaders.containsKey(appInfo.packageName)) { + return mClassLoaders.get(appInfo.packageName); } - ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader()); - mClassLoaders.put(pkg, classLoader); + + List<String> zipPaths = new ArrayList<>(); + List<String> libPaths = new ArrayList<>(); + LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths); + ClassLoader classLoader = new PathClassLoader( + TextUtils.join(File.pathSeparator, zipPaths), + TextUtils.join(File.pathSeparator, libPaths), + getParentClassLoader()); + mClassLoaders.put(appInfo.packageName, classLoader); return classLoader; } @@ -309,11 +321,6 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage return mParentClassLoader; } - public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException { - ClassLoader classLoader = getClassLoader(info.sourceDir, pkg); - return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader); - } - public <T> boolean dependsOn(Plugin p, Class<T> cls) { for (int i = 0; i < mPluginMap.size(); i++) { if (mPluginMap.valueAt(i).dependsOn(p, cls)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 9422101c76dd..f79ad71a114f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -541,7 +541,8 @@ public class NetworkControllerImpl extends BroadcastReceiver @VisibleForTesting void doUpdateMobileControllers() { - List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList(); + List<SubscriptionInfo> subscriptions = mSubscriptionManager + .getActiveSubscriptionInfoList(true); if (subscriptions == null) { subscriptions = Collections.emptyList(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java index 458377017fb9..3415c72230e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java @@ -91,7 +91,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { mMockPm = mock(PackageManager.class); mMockListener = mock(PluginListener.class); mMockManager = mock(PluginManagerImpl.class); - when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader()); + when(mMockManager.getClassLoader(any())).thenReturn(getClass().getClassLoader()); mMockEnabler = mock(PluginEnabler.class); when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler); mMockVersionInfo = mock(VersionInfo.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java index 76e68f1df724..536c043bd7ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java @@ -25,6 +25,7 @@ import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.test.suitebuilder.annotation.SmallTest; @@ -135,9 +136,13 @@ public class PluginManagerTest extends SysuiTestCase { }); resetExceptionHandler(); + String sourceDir = "myPlugin"; + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.sourceDir = sourceDir; + applicationInfo.packageName = WHITELISTED_PACKAGE; mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); - assertNull(mPluginManager.getOneShotPlugin("myPlugin", TestPlugin.class)); - assertNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE)); + assertNull(mPluginManager.getOneShotPlugin(sourceDir, TestPlugin.class)); + assertNull(mPluginManager.getClassLoader(applicationInfo)); } @Test @@ -152,9 +157,16 @@ public class PluginManagerTest extends SysuiTestCase { }); resetExceptionHandler(); + String sourceDir = "myPlugin"; + ApplicationInfo whiteListedApplicationInfo = new ApplicationInfo(); + whiteListedApplicationInfo.sourceDir = sourceDir; + whiteListedApplicationInfo.packageName = WHITELISTED_PACKAGE; + ApplicationInfo invalidApplicationInfo = new ApplicationInfo(); + invalidApplicationInfo.sourceDir = sourceDir; + invalidApplicationInfo.packageName = "com.android.invalidpackage"; mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); - assertNotNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE)); - assertNull(mPluginManager.getClassLoader("myPlugin", "com.android.invalidpackage")); + assertNotNull(mPluginManager.getClassLoader(whiteListedApplicationInfo)); + assertNull(mPluginManager.getClassLoader(invalidApplicationInfo)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index fdbf09083a4e..c1f88855ac24 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -182,6 +182,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { subs.add(subscription); } when(mMockSm.getActiveSubscriptionInfoList()).thenReturn(subs); + when(mMockSm.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(subs); mNetworkController.doUpdateMobileControllers(); } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 869d564b4329..312b83c56dea 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -1041,12 +1041,13 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onSetProperties(ProviderProperties properties) { - // move calls coming from below LMS onto a different thread to avoid deadlock - runInternal(() -> { - synchronized (mLock) { - mProperties = properties; - } - }); + // because this does not invoke any other methods which might result in calling back + // into the location provider, it is safe to run this on the calling thread. it is also + // currently necessary to run this on the calling thread to ensure that property changes + // are publicly visibly immediately, ie for mock providers which are created. + synchronized (mLock) { + mProperties = properties; + } } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 2340b77c4a3c..cb3f91b7b6bb 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -792,6 +792,16 @@ public final class DisplayManagerService extends SystemService { } } + private void setVirtualDisplayStateInternal(IBinder appToken, boolean isOn) { + synchronized (mSyncRoot) { + if (mVirtualDisplayAdapter == null) { + return; + } + + mVirtualDisplayAdapter.setVirtualDisplayStateLocked(appToken, isOn); + } + } + private void registerDefaultDisplayAdapters() { // Register default display adapters. synchronized (mSyncRoot) { @@ -1930,6 +1940,16 @@ public final class DisplayManagerService extends SystemService { } @Override // Binder call + public void setVirtualDisplayState(IVirtualDisplayCallback callback, boolean isOn) { + final long token = Binder.clearCallingIdentity(); + try { + setVirtualDisplayStateInternal(callback.asBinder(), isOn); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 5aa585fcad75..1ca8dd3af4e2 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -147,6 +147,13 @@ public class VirtualDisplayAdapter extends DisplayAdapter { return device; } + void setVirtualDisplayStateLocked(IBinder appToken, boolean isOn) { + VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); + if (device != null) { + device.setDisplayState(isOn); + } + } + /** * Returns the next unique index for the uniqueIdPrefix */ @@ -206,6 +213,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { private int mPendingChanges; private int mUniqueIndex; private Display.Mode mMode; + private boolean mIsDisplayOn; public VirtualDisplayDevice(IBinder displayToken, IBinder appToken, int ownerUid, String ownerPackageName, @@ -226,6 +234,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { mDisplayState = Display.STATE_UNKNOWN; mPendingChanges |= PENDING_SURFACE_CHANGE; mUniqueIndex = uniqueIndex; + mIsDisplayOn = surface != null; } @Override @@ -304,6 +313,14 @@ public class VirtualDisplayAdapter extends DisplayAdapter { } } + void setDisplayState(boolean isOn) { + if (mIsDisplayOn != isOn) { + mIsDisplayOn = isOn; + mInfo = null; + sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); + } + } + public void stopLocked() { setSurfaceLocked(null); mStopped = true; @@ -375,7 +392,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter { mInfo.type = Display.TYPE_VIRTUAL; mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ? DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL; - mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF; + + mInfo.state = mIsDisplayOn ? Display.STATE_ON : Display.STATE_OFF; + mInfo.ownerUid = mOwnerUid; mInfo.ownerPackageName = mOwnerPackageName; } diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java index ba21b7882afd..af716242210e 100755 --- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java @@ -51,6 +51,8 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { private static final int STATE_WAITING_FOR_OSD_NAME = 3; // State in which the action is waiting for gathering vendor id of non-local devices. private static final int STATE_WAITING_FOR_VENDOR_ID = 4; + // State in which the action is waiting for devices to be ready + private static final int STATE_WAITING_FOR_DEVICES = 5; /** * Interface used to report result of device discovery. @@ -89,16 +91,28 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { private final DeviceDiscoveryCallback mCallback; private int mProcessedDeviceCount = 0; private int mTimeoutRetry = 0; - private boolean mIsTvDevice = source().mService.isTvDevice(); + private boolean mIsTvDevice = localDevice().mService.isTvDevice(); + private final int mDelayPeriod; /** * Constructor. * * @param source an instance of {@link HdmiCecLocalDevice}. + * @param delay delay action for this period between query Physical Address and polling */ - DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) { + DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback, int delay) { super(source); mCallback = Preconditions.checkNotNull(callback); + mDelayPeriod = delay; + } + + /** + * Constructor. + * + * @param source an instance of {@link HdmiCecLocalDevice}. + */ + DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) { + this(source, callback, 0); } @Override @@ -117,7 +131,11 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { Slog.v(TAG, "Device detected: " + ackedAddress); allocateDevices(ackedAddress); - startPhysicalAddressStage(); + if (mDelayPeriod > 0) { + startToDelayAction(); + } else { + startPhysicalAddressStage(); + } } }, Constants.POLL_ITERATION_REVERSE_ORDER | Constants.POLL_STRATEGY_REMOTES_DEVICES, HdmiConfig.DEVICE_POLLING_RETRY); @@ -131,6 +149,13 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { } } + private void startToDelayAction() { + Slog.v(TAG, "Waiting for connected devices to be ready"); + mState = STATE_WAITING_FOR_DEVICES; + + checkAndProceedStage(); + } + private void startPhysicalAddressStage() { Slog.v(TAG, "Start [Physical Address Stage]:" + mDevices.size()); mProcessedDeviceCount = 0; @@ -159,6 +184,11 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { addTimer(mState, HdmiConfig.TIMEOUT_MS); } + private void delayActionWithTimePeriod(int timeDelay) { + mActionTimer.clearTimerMessage(); + addTimer(mState, timeDelay); + } + private void startOsdNameStage() { Slog.v(TAG, "Start [Osd Name Stage]:" + mDevices.size()); mProcessedDeviceCount = 0; @@ -385,6 +415,9 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { private void sendQueryCommand() { int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress; switch (mState) { + case STATE_WAITING_FOR_DEVICES: + delayActionWithTimePeriod(mDelayPeriod); + return; case STATE_WAITING_FOR_PHYSICAL_ADDRESS: queryPhysicalAddress(address); return; @@ -405,6 +438,10 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { return; } + if (mState == STATE_WAITING_FOR_DEVICES) { + startPhysicalAddressStage(); + return; + } if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) { sendQueryCommand(); return; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index e777ce8166ac..86be585e5d23 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -77,7 +77,7 @@ final class HdmiCecController { private static final int NUM_LOGICAL_ADDRESS = 16; - private static final int MAX_CEC_MESSAGE_HISTORY = 20; + private static final int MAX_CEC_MESSAGE_HISTORY = 200; // Predicate for whether the given logical address is remote device's one or not. private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() { @@ -682,7 +682,7 @@ final class HdmiCecController { void dump(final IndentingPrintWriter pw) { for (int i = 0; i < mLocalDevices.size(); ++i) { - pw.println("HdmiCecLocalDevice #" + i + ":"); + pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":"); pw.increaseIndent(); mLocalDevices.valueAt(i).dump(pw); pw.decreaseIndent(); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index 0e4e3342451c..5c1b3deb9955 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -26,22 +26,29 @@ import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.media.AudioDeviceInfo; +import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioSystem; import android.media.tv.TvContract; import android.os.SystemProperties; +import android.provider.Settings.Global; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.hdmi.Constants.AudioCodec; import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.stream.Collectors; + /** * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android @@ -83,10 +90,10 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); - mSystemAudioControlFeatureEnabled = true; - // TODO(amyjojo) make System Audio Control controllable by users - /*mSystemAudioControlFeatureEnabled = - mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/ + mRoutingControlFeatureEnabled = + mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, true); + mSystemAudioControlFeatureEnabled = + mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true); // TODO(amyjojo): make the map ro property. mTvInputs.put(Constants.CEC_SWITCH_HDMI1, "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5"); @@ -267,7 +274,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) { if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON) || ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON) - && lastSystemAudioControlStatus)) { + && lastSystemAudioControlStatus && isSystemAudioControlFeatureEnabled())) { addAndStartAction(new SystemAudioInitiationActionFromAvr(this)); } } @@ -400,8 +407,11 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected boolean handleGiveAudioStatus(HdmiCecMessage message) { assertRunOnServiceThread(); - - reportAudioStatus(message); + if (isSystemAudioControlFeatureEnabled()) { + reportAudioStatus(message.getSource()); + } else { + mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); + } return true; } @@ -478,14 +488,91 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { private byte[] getSupportedShortAudioDescriptors( AudioDeviceInfo deviceInfo, @AudioCodec int[] audioFormatCodes) { - // TODO(b/80297701) implement - return new byte[] {}; + ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length); + for (@AudioCodec int audioFormatCode : audioFormatCodes) { + byte[] sad = getSupportedShortAudioDescriptor(deviceInfo, audioFormatCode); + if (sad != null) { + if (sad.length == 3) { + + sads.add(sad); + } else { + HdmiLogger.warning( + "Dropping Short Audio Descriptor with length %d for requested codec %x", + sad.length, audioFormatCode); + } + } + } + // Short Audio Descriptors are always 3 bytes long. + byte[] bytes = new byte[sads.size() * 3]; + int index = 0; + for (byte[] sad : sads) { + System.arraycopy(sad, 0, bytes, index, 3); + index += 3; + } + return bytes; + } + + /** + * Returns a 3 byte short audio descriptor as described in CEC 1.4 table 29 or null if the + * audioFormatCode is not supported. + */ + @Nullable + private byte[] getSupportedShortAudioDescriptor( + AudioDeviceInfo deviceInfo, @AudioCodec int audioFormatCode) { + switch (audioFormatCode) { + case Constants.AUDIO_CODEC_NONE: { + return null; + } + case Constants.AUDIO_CODEC_LPCM: { + return getLpcmShortAudioDescriptor(deviceInfo); + } + // TODO(b/80297701): implement the rest of the codecs + case Constants.AUDIO_CODEC_DD: + case Constants.AUDIO_CODEC_MPEG1: + case Constants.AUDIO_CODEC_MP3: + case Constants.AUDIO_CODEC_MPEG2: + case Constants.AUDIO_CODEC_AAC: + case Constants.AUDIO_CODEC_DTS: + case Constants.AUDIO_CODEC_ATRAC: + case Constants.AUDIO_CODEC_ONEBITAUDIO: + case Constants.AUDIO_CODEC_DDP: + case Constants.AUDIO_CODEC_DTSHD: + case Constants.AUDIO_CODEC_TRUEHD: + case Constants.AUDIO_CODEC_DST: + case Constants.AUDIO_CODEC_WMAPRO: + default: { + return null; + } + } + } + + @Nullable + private byte[] getLpcmShortAudioDescriptor(AudioDeviceInfo deviceInfo) { + // TODO(b/80297701): implement + return null; } @Nullable private AudioDeviceInfo getSystemAudioDeviceInfo() { - // TODO(b/80297701) implement - // Get the audio device used for system audio mode. + AudioManager audioManager = mService.getContext().getSystemService(AudioManager.class); + if (audioManager == null) { + HdmiLogger.error( + "Error getting system audio device because AudioManager not available."); + return null; + } + AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS); + HdmiLogger.debug("Found %d audio input devices", devices.length); + for (AudioDeviceInfo device : devices) { + HdmiLogger.debug("%s at port %s", device.getProductName(), device.getPort()); + HdmiLogger.debug("Supported encodings are %s", + Arrays.stream(device.getEncodings()).mapToObj( + AudioFormat::toLogFriendlyEncoding + ).collect(Collectors.joining(", "))); + // TODO(b/80297701) use the actual device type that system audio mode is connected to. + if (device.getType() == AudioDeviceInfo.TYPE_HDMI_ARC) { + return device; + } + } return null; } @@ -583,17 +670,20 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { .setWiredDeviceConnectionState(AudioSystem.DEVICE_IN_HDMI, enabled ? 1 : 0, "", ""); } - private void reportAudioStatus(HdmiCecMessage message) { + void reportAudioStatus(int source) { assertRunOnServiceThread(); int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC); boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC); int maxVolume = mService.getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int minVolume = mService.getAudioManager().getStreamMinVolume(AudioManager.STREAM_MUSIC); int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume); + HdmiLogger.debug("Reporting volume %i (%i-%i) as CEC volume %i", volume, + minVolume, maxVolume, scaledVolume); mService.sendCecCommand( HdmiCecMessageBuilder.buildReportAudioStatus( - mAddress, message.getSource(), scaledVolume, mute)); + mAddress, source, scaledVolume, mute)); } /** @@ -688,6 +778,13 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device); } + void onSystemAduioControlFeatureSupportChanged(boolean enabled) { + setSystemAudioControlFeatureEnabled(enabled); + if (enabled) { + addAndStartAction(new SystemAudioInitiationActionFromAvr(this)); + } + } + @ServiceThreadOnly void setSystemAudioControlFeatureEnabled(boolean enabled) { assertRunOnServiceThread(); @@ -697,6 +794,14 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { } @ServiceThreadOnly + void setRoutingControlFeatureEnables(boolean enabled) { + assertRunOnServiceThread(); + synchronized (mLock) { + mRoutingControlFeatureEnabled = enabled; + } + } + + @ServiceThreadOnly void doManualPortSwitching(int portId, IHdmiControlCallback callback) { assertRunOnServiceThread(); // TODO: validate port ID @@ -831,6 +936,10 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { } protected void routeToInputFromPortId(int portId) { + if (!isRoutingControlFeatureEnabled()) { + HdmiLogger.debug("Routing Control Feature is not enabled."); + return; + } if (mArcIntentUsed) { routeToTvInputFromPortId(portId); } else { @@ -978,4 +1087,20 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { } mDeviceInfos.clear(); } + + @Override + protected void dump(IndentingPrintWriter pw) { + pw.println("HdmiCecLocalDeviceAudioSystem:"); + pw.increaseIndent(); + pw.println("mSystemAudioActivated: " + mSystemAudioActivated); + pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled); + pw.println("mTvSystemAudioModeSupport: " + mTvSystemAudioModeSupport); + pw.println("mArcEstablished: " + mArcEstablished); + pw.println("mArcIntentUsed: " + mArcIntentUsed); + HdmiUtils.dumpMap(pw, "mTvInputs:", mTvInputs); + HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos); + pw.decreaseIndent(); + super.dump(pw); + } + } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index a95f7f1f3f3d..cbddaf53348a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -66,6 +66,10 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @LocalActivePort protected int mLocalActivePort = Constants.CEC_SWITCH_HOME; + // Whether the Routing Coutrol feature is enabled or not. True by default. + @GuardedBy("mLock") + protected boolean mRoutingControlFeatureEnabled; + protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) { super(service, deviceType); } @@ -123,7 +127,9 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { } setIsActiveSource(physicalAddress == mService.getPhysicalAddress()); updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON); - switchInputOnReceivingNewActivePath(physicalAddress); + if (isRoutingControlFeatureEnabled()) { + switchInputOnReceivingNewActivePath(physicalAddress); + } return true; } @@ -153,6 +159,10 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleRoutingChange(HdmiCecMessage message) { assertRunOnServiceThread(); + if (!isRoutingControlFeatureEnabled()) { + mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); + return true; + } int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2); // if the current device is a pure playback device if (!mIsSwitchDevice @@ -168,6 +178,10 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleRoutingInformation(HdmiCecMessage message) { assertRunOnServiceThread(); + if (!isRoutingControlFeatureEnabled()) { + mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); + return true; + } int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); // if the current device is a pure playback device if (!mIsSwitchDevice @@ -279,6 +293,12 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { } } + boolean isRoutingControlFeatureEnabled() { + synchronized (mLock) { + return mRoutingControlFeatureEnabled; + } + } + // Check if the device is trying to switch to the same input that is active right now. // This can help avoid redundant port switching. protected boolean isSwitchingToTheSameInput(@LocalActivePort int activePort) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java index c0056158a9c5..f8b39627f236 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java @@ -17,6 +17,7 @@ package com.android.server.hdmi; import android.annotation.Nullable; + import libcore.util.EmptyArray; import java.util.Arrays; @@ -111,12 +112,11 @@ public final class HdmiCecMessage { @Override public String toString() { StringBuffer s = new StringBuffer(); - s.append(String.format("<%s> src: %d, dst: %d", - opcodeToString(mOpcode), mSource, mDestination)); + s.append(String.format("<%s> %X%X:%02X", + opcodeToString(mOpcode), mSource, mDestination, mOpcode)); if (mParams.length > 0) { - s.append(", params:"); for (byte data : mParams) { - s.append(String.format(" %02X", data)); + s.append(String.format(":%02X", data)); } } return s.toString(); @@ -133,7 +133,7 @@ public final class HdmiCecMessage { case Constants.MESSAGE_TUNER_STEP_DECREMENT: return "Tuner Step Decrement"; case Constants.MESSAGE_TUNER_DEVICE_STATUS: - return "Tuner Device Staus"; + return "Tuner Device Status"; case Constants.MESSAGE_GIVE_TUNER_DEVICE_STATUS: return "Give Tuner Device Status"; case Constants.MESSAGE_RECORD_ON: @@ -207,7 +207,7 @@ public final class HdmiCecMessage { case Constants.MESSAGE_DEVICE_VENDOR_ID: return "Device Vendor Id"; case Constants.MESSAGE_VENDOR_COMMAND: - return "Vendor Commandn"; + return "Vendor Command"; case Constants.MESSAGE_VENDOR_REMOTE_BUTTON_DOWN: return "Vendor Remote Button Down"; case Constants.MESSAGE_VENDOR_REMOTE_BUTTON_UP: @@ -215,7 +215,7 @@ public final class HdmiCecMessage { case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID: return "Give Device Vendor Id"; case Constants.MESSAGE_MENU_REQUEST: - return "Menu REquest"; + return "Menu Request"; case Constants.MESSAGE_MENU_STATUS: return "Menu Status"; case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS: @@ -247,7 +247,7 @@ public final class HdmiCecMessage { case Constants.MESSAGE_SET_EXTERNAL_TIMER: return "Set External Timer"; case Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR: - return "Repot Short Audio Descriptor"; + return "Report Short Audio Descriptor"; case Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR: return "Request Short Audio Descriptor"; case Constants.MESSAGE_INITIATE_ARC: diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 2d6e76294e4a..aabe1ad659d6 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -26,6 +26,7 @@ import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE; import static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING; import static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE; import static com.android.server.hdmi.Constants.OPTION_MHL_SERVICE_CONTROL; +import static com.android.server.power.ShutdownThread.SHUTDOWN_ACTION_PROPERTY; import android.annotation.Nullable; import android.content.BroadcastReceiver; @@ -184,9 +185,10 @@ public class HdmiControlService extends SystemService { @Override public void onReceive(Context context, Intent intent) { assertRunOnServiceThread(); + boolean isReboot = SystemProperties.get(SHUTDOWN_ACTION_PROPERTY).contains("1"); switch (intent.getAction()) { case Intent.ACTION_SCREEN_OFF: - if (isPowerOnOrTransient()) { + if (isPowerOnOrTransient() && !isReboot) { onStandby(STANDBY_SCREEN_OFF); } break; @@ -202,7 +204,7 @@ public class HdmiControlService extends SystemService { } break; case Intent.ACTION_SHUTDOWN: - if (isPowerOnOrTransient()) { + if (isPowerOnOrTransient() && !isReboot) { onStandby(STANDBY_SHUTDOWN); } break; @@ -345,6 +347,10 @@ public class HdmiControlService extends SystemService { @Nullable private Looper mIoLooper; + // Thread safe physical address + @GuardedBy("mLock") + private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS; + // Last input port before switching to the MHL port. Should switch back to this port // when the mobile device sends the request one touch play with off. // Gets invalidated if we go to other port/input. @@ -564,7 +570,8 @@ public class HdmiControlService extends SystemService { Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, Global.MHL_INPUT_SWITCHING_ENABLED, - Global.MHL_POWER_CHARGE_ENABLED + Global.MHL_POWER_CHARGE_ENABLED, + Global.HDMI_CEC_SWITCH_ENABLED }; for (String s : settings) { resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver, @@ -605,6 +612,14 @@ public class HdmiControlService extends SystemService { if (isTvDeviceEnabled()) { tv().setSystemAudioControlFeatureEnabled(enabled); } + if (isAudioSystemDevice()) { + audioSystem().onSystemAduioControlFeatureSupportChanged(enabled); + } + break; + case Global.HDMI_CEC_SWITCH_ENABLED: + if (isAudioSystemDevice()) { + audioSystem().setRoutingControlFeatureEnables(enabled); + } break; case Global.MHL_INPUT_SWITCHING_ENABLED: setMhlInputChangeEnabled(enabled); @@ -734,6 +749,10 @@ public class HdmiControlService extends SystemService { assertRunOnServiceThread(); HdmiPortInfo[] cecPortInfo = null; + synchronized (mLock) { + mPhysicalAddress = getPhysicalAddress(); + } + // CEC HAL provides majority of the info while MHL does only MHL support flag for // each port. Return empty array if CEC HAL didn't provide the info. if (mCecController != null) { @@ -1532,6 +1551,14 @@ public class HdmiControlService extends SystemService { } @Override + public int getPhysicalAddress() { + enforceAccessPermission(); + synchronized (mLock) { + return mPhysicalAddress; + } + } + + @Override public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) { enforceAccessPermission(); runOnServiceThread(new Runnable() { @@ -1834,14 +1861,28 @@ public class HdmiControlService extends SystemService { Slog.w(TAG, "audio system is not in system audio mode"); return; } - int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume); - - sendCecCommand(HdmiCecMessageBuilder - .buildReportAudioStatus( - device.getDeviceInfo().getLogicalAddress(), - Constants.ADDR_TV, - scaledVolume, - isMute)); + audioSystem().reportAudioStatus(Constants.ADDR_TV); + } + }); + } + + @Override + public void setSystemAudioModeOnForAudioOnlySource() { + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + if (!isAudioSystemDevice()) { + Slog.e(TAG, "Not an audio system device. Won't set system audio mode on"); + return; + } + if (!audioSystem().checkSupportAndSetSystemAudioMode(true)) { + Slog.e(TAG, "System Audio Mode is not supported."); + return; + } + sendCecCommand( + HdmiCecMessageBuilder.buildSetSystemAudioMode( + audioSystem().mAddress, Constants.ADDR_BROADCAST, true)); } }); } @@ -1851,27 +1892,28 @@ public class HdmiControlService extends SystemService { if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return; final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled); pw.println("mProhibitMode: " + mProhibitMode); - if (mCecController != null) { - pw.println("mCecController: "); - pw.increaseIndent(); - mCecController.dump(pw); - pw.decreaseIndent(); - } + pw.println("mPowerStatus: " + mPowerStatus); + + // System settings + pw.println("System_settings:"); + pw.increaseIndent(); + pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled); + pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled); + pw.decreaseIndent(); pw.println("mMhlController: "); pw.increaseIndent(); mMhlController.dump(pw); pw.decreaseIndent(); - pw.println("mPortInfo: "); - pw.increaseIndent(); - for (HdmiPortInfo hdmiPortInfo : mPortInfo) { - pw.println("- " + hdmiPortInfo); + HdmiUtils.dumpIterable(pw, "mPortInfo:", mPortInfo); + if (mCecController != null) { + pw.println("mCecController: "); + pw.increaseIndent(); + mCecController.dump(pw); + pw.decreaseIndent(); } - pw.decreaseIndent(); - pw.println("mPowerStatus: " + mPowerStatus); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index 2a8117fac68d..21106828d43f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -20,9 +20,13 @@ import android.hardware.hdmi.HdmiDeviceInfo; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.util.IndentingPrintWriter; + import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; + /** * Various utilities to handle HDMI CEC messages. @@ -317,4 +321,74 @@ final class HdmiUtils { info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(), info.getVendorId(), info.getDisplayName(), newPowerStatus); } + + /** + * Dump a {@link SparseArray} to the print writer. + * + * <p>The dump is formatted: + * <pre> + * name: + * key = value + * key = value + * ... + * </pre> + */ + static <T> void dumpSparseArray(IndentingPrintWriter pw, String name, + SparseArray<T> sparseArray) { + printWithTrailingColon(pw, name); + pw.increaseIndent(); + int size = sparseArray.size(); + for (int i = 0; i < size; i++) { + int key = sparseArray.keyAt(i); + T value = sparseArray.get(key); + pw.printPair(Integer.toString(key), value); + pw.println(); + } + pw.decreaseIndent(); + } + + private static void printWithTrailingColon(IndentingPrintWriter pw, String name) { + pw.println(name.endsWith(":") ? name : name.concat(":")); + } + + /** + * Dump a {@link Map} to the print writer. + * + * <p>The dump is formatted: + * <pre> + * name: + * key = value + * key = value + * ... + * </pre> + */ + static <K, V> void dumpMap(IndentingPrintWriter pw, String name, Map<K, V> map) { + printWithTrailingColon(pw, name); + pw.increaseIndent(); + for (Map.Entry<K, V> entry: map.entrySet()) { + pw.printPair(entry.getKey().toString(), entry.getValue()); + pw.println(); + } + pw.decreaseIndent(); + } + + /** + * Dump a {@link Map} to the print writer. + * + * <p>The dump is formatted: + * <pre> + * name: + * value + * value + * ... + * </pre> + */ + static <T> void dumpIterable(IndentingPrintWriter pw, String name, Iterable<T> values) { + printWithTrailingColon(pw, name); + pw.increaseIndent(); + for (T value : values) { + pw.println(value); + } + pw.decreaseIndent(); + } } diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java index 86bc9f313551..fe91c63e712b 100644 --- a/services/core/java/com/android/server/location/MockProvider.java +++ b/services/core/java/com/android/server/location/MockProvider.java @@ -52,7 +52,6 @@ public class MockProvider extends AbstractLocationProvider { mExtras = null; setProperties(properties); - setEnabled(true); } /** Sets the enabled state of this mock provider. */ diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 5f56fe59c1f2..177f244129f4 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -462,22 +462,19 @@ class KeyguardController { mOccluded = false; mDismissingKeyguardActivity = null; - // Only the top activity of the focused stack on each display may control it's - // occluded state. - final ActivityStack focusedStack = display.getFocusedStack(); - if (focusedStack != null) { - final ActivityRecord topDismissing = - focusedStack.getTopDismissingKeyguardActivity(); - mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null - && focusedStack.topRunningActivityLocked() == topDismissing - && controller.canShowWhileOccluded( + final ActivityStack stack = getStackForControllingOccluding(display); + if (stack != null) { + final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity(); + mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null + && stack.topRunningActivityLocked() == topDismissing + && controller.canShowWhileOccluded( true /* dismissKeyguard */, false /* showWhenLocked */)); - if (focusedStack.getTopDismissingKeyguardActivity() != null) { - mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity(); + if (stack.getTopDismissingKeyguardActivity() != null) { + mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity(); } - mOccluded |= controller.mWindowManager.isShowingDream(); } + mOccluded |= controller.mWindowManager.isShowingDream(); // TODO(b/113840485): Handle app transition for individual display, and apply occluded // state change to secondary displays. @@ -492,6 +489,23 @@ class KeyguardController { } } + /** + * Gets the stack used to check the occluded state. + * <p> + * Only the top non-pinned activity of the focusable stack on each display can control its + * occlusion state. + */ + private ActivityStack getStackForControllingOccluding(ActivityDisplay display) { + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + if (stack != null && stack.isFocusableAndVisible() + && !stack.inPinnedWindowingMode()) { + return stack; + } + } + return null; + } + void dumpStatus(PrintWriter pw, String prefix) { final StringBuilder sb = new StringBuilder(); sb.append(prefix); diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index b290bc516320..bce944d88a73 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -856,7 +856,7 @@ struct GnssMeasurementCallback : public IGnssMeasurementCallback_V2_0 { Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_0( const IGnssMeasurementCallback_V2_0::GnssData& data) { - // TODO(b/119571122): implement gnssMeasurementCb_2_0 + translateAndSetGnssData(data); return Void(); } @@ -894,9 +894,8 @@ size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_ return data.measurementCount; } -template<> -size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_1::GnssData> - (const IGnssMeasurementCallback_V1_1::GnssData& data) { +template<class T> +size_t GnssMeasurementCallback::getMeasurementCount(const T& data) { return data.measurements.size(); } @@ -958,6 +957,17 @@ void GnssMeasurementCallback::translateSingleGnssMeasurement ADR_STATE_HALF_CYCLE_REPORTED)); } +// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); +template<> +void GnssMeasurementCallback::translateSingleGnssMeasurement + <IGnssMeasurementCallback_V2_0::GnssMeasurement>( + const IGnssMeasurementCallback_V2_0::GnssMeasurement* measurement_V2_0, + JavaObject& object) { + translateSingleGnssMeasurement(&(measurement_V2_0->v1_1), object); + + SET(CodeType, (static_cast<int32_t>(measurement_V2_0->codeType))); +} + jobject GnssMeasurementCallback::translateGnssClock( JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssClock* clock) { JavaObject object(env, "android/location/GnssClock"); |